我有一个python类,关于以下内容,重要的部分是ctypes库。
from ctypes import *
class PyClassExample:
def __init__(self):
self.path = '/some/unix/path/file.so'
self.lib = CDLL(self.path) # non-serializable element
self.arr = [1, 2, 3, 4] # serializable element
def __getstate__(self):
state = self.__dict__.copy()
del state['lib']
return state
当我尝试在它的实例化对象上调用json.dump
时,我得到一个错误消息:
TypeError: PyClassExample类型的对象不是JSON可序列化的
为什么json.dump
不调用__getstate__
而pickle.dump
调用它?json.dump
使用什么机制来获取传递对象的可序列化状态?
包含__getstate__
的协议是为Pickle设计的,它被设计成能够序列化几乎任何Python对象。它是一种灵活的格式,可以存储对象类型及其状态。
JSON没有这个功能。它只支持一组固定的类型,扩展格式通常是通过在后处理步骤中解释值来完成的(比如在dict
中读取预定义的键)。你可以这样做:
import json
def my_default(o):
if isinstance(o, PyClassExample):
return {'$type': 'PyClassExample', 'path': o.path, 'arr': o.arr}
raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable")
def my_object_hook(o):
if o.get('$type') == 'PyClassExample':
# FIXME: implement an __init__ that supports this
return PyClassExample(o)
return o
然后用json.dump(..., default=my_default)
代替对json.dump(...)
的调用,用json.load(..., object_hook=my_object_hook)
代替对json.load(...)
的调用。
如果你想支持更多的类,你可能想要一个更通用的解决方案,像一个协议"JSON-able"类。如果你真的想,你甚至可以滥用__getstate__
,像这样:
import json
# Make all classes you want to be able to serialise that need to use this mechanism a subclass of JSONAble
class JSONAble:
registry = {}
def __init_subclass__(cls, /, **kwargs):
super().__init_subclass__(**kwargs)
JSONAble.registry[cls.__name__] = cls
def my_default(o):
if isinstance(o, JSONAble):
return {'(type)': type(o).__name__, **o.__getstate__()}
raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable")
def my_object_hook(o):
if '(type)' in o:
new_o = JSONAble.registry[o.pop('(type)')]()
new_o.__dict__ = o
return new_o
return o