由于你正在处理一棵树形结构,使用嵌套字典是很自然的。下面的代码片段创建了一个 dict
的子类,并将其自身作为实例的底层 __dict__
—— 这是我在许多不同情境中遇到的一个有趣且有用的技巧:
返回匿名类还是对象用来作为“结构体”更好? (stackoverflow)
如何使用点号“.”访问字典成员? (stackoverflow)
jsobject.py
(PyDoc.net)
制造像 JavaScript 对象一样的 Python 对象
(James Robert's 博客)
AttrDict
(ActiveState 配方)
带有属性访问方式的字典
(ActiveState 配方)
事实上,这个技巧在 Python 中如此频繁,以至于我认为它是一个(不太知名的)Python习惯用法。
class TreeNode(dict):
def __init__(self, name, children=None):
super().__init__()
self.__dict__ = self
self.name = name
self.children = list(children) if children is not None else []
这解决了序列化问题的一半,但当使用
json.loads()
读取生成的数据时,它将变成一个普通的字典对象,而不是
TreeNode
的实例。这是因为
JSONEncoder
可以自己编码字典(及其子类)。
解决这个问题的一种方法是,在
TreeNode
类中添加一种另类构造方法,可以调用该方法从
json.loads()
返回的嵌套字典中重建数据结构。
以下是我的意思:
...
@staticmethod
def from_dict(dict_):
""" Recursively (re)construct TreeNode-based tree from dictionary. """
node = TreeNode(dict_['name'], dict_['children'])
node.children = list(map(TreeNode.from_dict, node.children))
return node
if __name__ == '__main__':
import json
tree = TreeNode('Parent')
tree.children.append(TreeNode('Child 1'))
child2 = TreeNode('Child 2')
tree.children.append(child2)
child2.children.append(TreeNode('Grand Kid'))
child2.children[0].children.append(TreeNode('Great Grand Kid'))
json_str = json.dumps(tree, indent=2)
print(json_str)
print()
pyobj = TreeNode.from_dict(json.loads(json_str))
print('pyobj class: {}'.format(pyobj.__class__.__name__))
print(json.dumps(pyobj, indent=2))
输出:
{
"name": "Parent",
"children": [
{
"name": "Child 1",
"children": []
},
{
"name": "Child 2",
"children": [
{
"name": "Grand Kid",
"children": [
{
"name": "Great Grand Kid",
"children": []
}
]
}
]
}
]
}
pyobj class: TreeNode
{
"name": "Parent",
"children": [
{
"name": "Child 1",
"children": []
},
{
"name": "Child 2",
"children": [
{
"name": "Grand Kid",
"children": [
{
"name": "Great Grand Kid",
"children": []
}
]
}
]
}
]
}