目前主要的序列化模块是pickle和json。
接下来,我们把之前的我们自定义的树节点TreeNode类进行序列化。
TreeNode类代码参见Python 数据结构 tree 树
pickle 可能是使用最多的序列化模块了。
cPickle是它的 C 语言实现,相比具有较好的性能。(推荐优先使用 cPickle)
pickle提供的接口
由于pickle支持的数据类型广泛
__dict__
或者__getstate__()
可以返回序列化对象的实例不能支持的数据类型
__getstate__()
和 __setstate__()
进行手动操作)由于我们的TreeNode类内部的属性都是简单的对象,所以可以使用pickle直接进行序列化。
# build a tree
root = TreeNode('') # root name is ''
a1 = root.add_child('a1')
a1.add_child('b1')
a1.add_child('b2')
a2 = root.add_child('a2')
b3 = a2.add_child('b3')
b3.add_child('c1')
root.dump()
# serialize to a string
s = pickle.dumps(root)
print(s)
# deserialize from the string
new_root = pickle.loads(s)
new_root.dump()
可以看到new_root.dump()
的结果和之前的root.dump()
一样。
但是序列化的结果并不是易于阅读和手工修改, 例如:
(i__main__
TreeNode
p1
(dp2
S'name'
p3
V
sS'parent'
p4
NsS'child'
p5
(dp6
Va1
p7
(i__main__
TreeNode
......
如果包含不能序列化的对象:
- 序列化的时候,需要用到__getstate__()
手工剔除
- 反序列化的时候,通过__setstate__()
手工加载
例如:假设我们的TreeNode有一个文件句柄属性fp
class TreeNode():
"""The basic node of tree structure"""
def __init__(self, name, parent=None):
# super(TreeNode, self).__init__()
self.name = name
self.parent = parent
self.child = {}
self.fpath = None
self.fp = None # attr can not be serialized
def __getstate__(self):
"""return a dict for current status"""
state = self.__dict__.copy()
del state['fp'] # manually delete
return state
def __setstate__(self, state):
"""return a dict for current status"""
self.__dict__.update(state)
self.fp = open(self.fpath) # manually update
return state
上面的__getstate__()
和__setstate__()
会在pickle的dump()
和load()
中被自动调用。
JSON ( JavaScript Object Notation )是一种轻量级数据交换格式。
由于其格式使用了其他许多流行编程的约定,如 C 、 C++ 、 C#、 Java 、 JS 、 Python 等,加之其简单灵活、可读性和互操作性较强、易于解析和使用等特点,逐渐变得流行起来。
由于TreeNode类是我们自定义的类结构,这里需要我们自己提供用于编码的解码的类。
try: import simplejson as json
except ImportError: import json
class TreeNodeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, TreeNode):
return {
'treenode_name': obj.name,
'child': {key: val for key, val in obj.items() },
}
return json.JSONEncoder.default(self, obj)
def TreeNodeDecoder(obj):
if isinstance(obj, dict) and 'treenode_name' in obj:
newobj = TreeNode(obj['treenode_name'])
for name, subobj in obj.get('child', {}).items():
newobj.add_child(name, TreeNodeDecoder(subobj))
return newobj
return obj
NOTE: 为什么在上面的代码里用treenode_name
这种丑陋的名字,而不是用name
?
因为在Decoder的时候,需要判断那个object是我们的TreeNode dict。好无奈#_#
当我们需要序列化的时候,
print('test json dumps()')
s = json.dumps(root, cls=TreeNodeEncoder, indent=4, sort_keys=True)
print(s)
print('test json loads()')
new_root = json.loads(s, object_hook=TreeNodeDecoder)
new_root.dump()
序列化时通过参数cls
来指定自定义的编码类,反序列化的时候通过object_hook
来指定object的处理函数。
序列化时参数indent
和sort_keys
可以使序列化后的字符串更易于阅读和查找,例如:
{
"child": {
"a1": {
"child": {
"b1": {
"child": {},
"treenode_name": "b1" },
"b2": {
"child": {},
"treenode_name": "b2" }
},
"treenode_name": "a1"
},
"a2": {
"child": {
"b3": {
"child": { "c1": { "child": {}, "treenode_name": "c1" } },
"treenode_name": "b3" }
},
"treenode_name": "a2"
}
},
"treenode_name": ""
}