python - 序列化和反序列化来自用户定义类的对象
问题描述
假设我有这样的类层次结构:
class SerializableWidget(object):
# some code
class WidgetA(SerilizableWidget):
# some code
class WidgetB(SerilizableWidget):
# some code
我希望能够将WidgetA
和WidgetB
(以及可能的其他小部件)的实例序列化为文本文件为json
. 然后,我希望能够在不事先知道它们的具体类的情况下反序列化它们:
some_widget = deserielize_from_file(file_path) # pseudocode, doesn't have to be exactly a method like this
并且some_widget
需要从 的精确子类构造SerilizableWidget
。我该怎么做呢?我需要在层次结构的每个类中覆盖/实现哪些方法?
假设上述类的所有字段都是原始类型。我如何覆盖一些__to_json__
和__from_json__
方法,类似的东西?
解决方案
你可以用很多方法解决这个问题。一个例子是分别使用object_hook
和default
参数来json.load
和json.dump
。
您所需要做的就是将类与对象的序列化版本一起存储,然后在加载时必须使用哪个类与哪个名称对应的映射。
下面的例子使用dispatcher
类装饰器在序列化的时候存储类名和对象,反序列化的时候再查找。您所需要的只是_as_dict
每个类上的一个方法来将数据转换为字典:
import json
@dispatcher
class Parent(object):
def __init__(self, name):
self.name = name
def _as_dict(self):
return {'name': self.name}
@dispatcher
class Child1(Parent):
def __init__(self, name, n=0):
super().__init__(name)
self.n = n
def _as_dict(self):
d = super()._as_dict()
d['n'] = self.n
return d
@dispatcher
class Child2(Parent):
def __init__(self, name, k='ok'):
super().__init__(name)
self.k = k
def _as_dict(self):
d = super()._as_dict()
d['k'] = self.k
return d
现在进行测试。首先让我们创建一个包含 3 个不同类型对象的列表。
>>> obj = [Parent('foo'), Child1('bar', 15), Child2('baz', 'works')]
序列化它将产生每个对象中具有类名的数据:
>>> s = json.dumps(obj, default=dispatcher.encoder_default)
>>> print(s)
[
{"__class__": "Parent", "name": "foo"},
{"__class__": "Child1", "name": "bar", "n": 15},
{"__class__": "Child2", "name": "baz", "k": "works"}
]
并将其加载回来会生成正确的对象:
obj2 = json.loads(s, object_hook=dispatcher.decoder_hook)
print(obj2)
[
<__main__.Parent object at 0x7fb6cd561cf8>,
<__main__.Child1 object at 0x7fb6cd561d68>,
<__main__.Child2 object at 0x7fb6cd561e10>
]
最后,这里的实现dispatcher
:
class _Dispatcher:
def __init__(self, classname_key='__class__'):
self._key = classname_key
self._classes = {} # to keep a reference to the classes used
def __call__(self, class_): # decorate a class
self._classes[class_.__name__] = class_
return class_
def decoder_hook(self, d):
classname = d.pop(self._key, None)
if classname:
return self._classes[classname](**d)
return d
def encoder_default(self, obj):
d = obj._as_dict()
d[self._key] = type(obj).__name__
return d
dispatcher = _Dispatcher()
推荐阅读
- linux - Qt5 的 CMAKE_PREFIX_PATH
- r - 如何通过特定键重新排序 ggplot2::facet_wrap 构面?
- android - 在 Android Studio 的编辑器左侧显示 git 更改
- c - 即使我添加了显式类型转换,Clang 优化也会改变 C 中的计算结果
- javascript - 通过翻阅单词动态更改文本
- twilio - twilio 获取排队调用的自定义参数
- r - 如何在 R 中的函数中添加自定义错误消息
- python-3.x - 如何在处理rasa中的按钮时禁用用户输入?
- python - 如何修复我的类代码“int”对象错误?
- python - 从 UTC 转换到 Python 中的任何其他时区后如何获取确切时间?