7.1.5 循环引用
pickle协议会自动处理对象之间的循环引用,所以复杂数据结构不需要任何特殊的处理。考虑图7-1中的有向图。这个图中包含几个环,不过仍然可以腌制所得的正确结构然后重新加载。
import pickle
class Node:
"""A simple digraph
"""
def __init__(self,name):
self.name = name
self.connections = []
def add_edge(self,node):
"Create an edge between this node and the other."
self.connections.append(node)
def __iter__(self):
return iter(self.connections)
def preorder_traversal(root,seen=None,parent=None):
"""Generator function to yield the edges in a graph.
"""
if seen is None:
seen = set()
yield (parent,root)
if root in seen:
return
seen.add(root)
for node in root:
recurse = preorder_traversal(node,seen,root)
for parent,subnode in recurse:
yield (parent,subnode)
def show_edges(root):
"Print all the edges in the graph."
for parent,child in preorder_traversal(root):
if not parent:
continue
print('{:>5} -> {:>2} ({})'.format(
parent.name,child.name,id(child)))
# Set up the nodes.
root = Node('root')
a = Node('a')
b = Node('b')
c = Node('c')
# Add edges between them.
root.add_edge(a)
root.add_edge(b)
a.add_edge(b)
b.add_edge(a)
b.add_edge(c)
a.add_edge(a)
print('ORIGINAL GRAPH:')
show_edges(root)
# Pickle and unpickle the graph to create
# a new set of nodes.
dumped = pickle.dumps(root)
reloaded = pickle.loads(dumped)
print('\nRELOADED GRAPH:')
show_edges(reloaded)
重新加载的节点并不是同一个对象,但保持了节点之间关系,而且如果对象有多个引用,那么只会重新加载这个对象的一个副本。要验证这两点,可以在通过pickle传递节点之前和之后检查节点的id()值。
运行结果: