承接上文 pickle和cPickle:Python對象的序列化(上) 。node
當與你本身的類一塊兒工做時,你必須保證類被醃漬出如今讀取pickle的進程的命名空間中。只有該實例的數據而不是類定義被醃漬。類名被用於在反醃漬時,找到構造器(constructor)以建立新對象。以此——往一個文件寫入一個類的實例爲例:python
pythontry: import cPickle as pickle except: import pickle import sys class SimpleObject(object): def __init__(self, name): self.name = name l = list(name) l.reverse() self.name_backwards = ''.join(l) return if __name__ == '__main__': data = [] data.append(SimpleObject('pickle')) data.append(SimpleObject('cPickle')) data.append(SimpleObject('last')) try: filename = sys.argv[1] except IndexError: raise RuntimeError('Please specify a filename as an argument to %s' % sys.argv[0]) out_s = open(filename, 'wb') try: # 寫入流中 for o in data: print 'WRITING: %s (%s)' % (o.name, o.name_backwards) pickle.dump(o, out_s) finally: out_s.close()
在運行時,該腳本建立一個以在命令行指定的參數爲名的文件:數據庫
python$ python pickle_dump_to_file_1.py test.dat WRITING: pickle (elkcip) WRITING: cPickle (elkciPc) WRITING: last (tsal)
一個在讀取結果醃漬對象失敗的簡化嘗試:segmentfault
pythontry: import cPickle as pickle except: import pickle import pprint from StringIO import StringIO import sys try: filename = sys.argv[1] except IndexError: raise RuntimeError('Please specify a filename as an argument to %s' % sys.argv[0]) in_s = open(filename, 'rb') try: # 讀取數據 while True: try: o = pickle.load(in_s) except EOFError: break else: print 'READ: %s (%s)' % (o.name, o.name_backwards) finally: in_s.close()
該版本失敗的緣由在於沒有 SimpleObject 類可用:app
python$ python pickle_load_from_file_1.py test.dat Traceback (most recent call last): File "pickle_load_from_file_1.py", line 52, in <module> o = pickle.load(in_s) AttributeError: 'module' object has no attribute 'SimpleObject'
正確的版本從原腳本中導入 SimpleObject ,可成功運行。
添加:函數
pythonfrom pickle_dump_to_file_1 import SimpleObject
至導入列表的尾部,接着從新運行該腳本:測試
python$ python pickle_load_from_file_2.py test.dat READ: pickle (elkcip) READ: cPickle (elkciPc) READ: last (tsal)
當醃漬有值的數據類型不能被醃漬時(套接字、文件句柄(file handles)、數據庫鏈接等之類的),有一些特別的考慮。由於使用值而不能被醃漬的類,能夠定義 __getstate__()
和 __setstate__()
來返回狀態(state)的一個子集,才能被醃漬。新式類(New-style classes)也能夠定義__getnewargs__()
,該函數應當返回被傳遞至類內存分配器(the class memory allocator)(C.__new__()
)的參數。使用這些新特性的更多細節,包含在標準庫文檔中。spa
pickle協議(pickle protocol)自動處理對象間的環形引用,所以,即便是很複雜的對象,你也不用特別爲此作什麼。考慮下面這個圖:命令行
上圖雖然包括幾個環形引用,但也能以正確的結構醃漬和從新讀取(reloaded)。code
pythonimport pickle class Node(object): """ 一個全部結點均可知它所連通的其它結點的簡單有向圖。 """ def __init__(self, name): self.name = name self.connections = [] return def add_edge(self, node): "建立兩個結點之間的一條邊。" self.connections.append(node) return def __iter__(self): return iter(self.connections) def preorder_traversal(root, seen=None, parent=None): """產生器(Generator )函數經過一個先根遍歷(preorder traversal)生成(yield)邊。""" if seen is None: seen = set() yield (parent, root) if root in seen: return seen.add(root) for node in root: for (parent, subnode) in preorder_traversal(node, seen, root): yield (parent, subnode) return def show_edges(root): "打印圖中的全部邊。" for parent, child in preorder_traversal(root): if not parent: continue print '%5s -> %2s (%s)' % (parent.name, child.name, id(child)) # 建立結點。 root = Node('root') a = Node('a') b = Node('b') c = Node('c') # 添加邊。 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) # 醃漬和反醃漬該圖來建立 # 一個結點集合。 dumped = pickle.dumps(root) reloaded = pickle.loads(dumped) print print 'RELOADED GRAPH:' show_edges(reloaded)
從新讀取的諸多節點(譯者注:對應圖中的圓圈)再也不是同一個對象,可是節點間的關係保持住了,並且讀取的僅僅是帶有多個引用的對象的一個拷貝。上面所說的能夠經過測試各節點在pickle處理前和以後的id()
值來驗證。
python$ python pickle_cycle.py ORIGINAL GRAPH: root -> a (4299721744) a -> b (4299721808) b -> a (4299721744) b -> c (4299721872) a -> a (4299721744) root -> b (4299721808) RELOADED GRAPH: root -> a (4299722000) a -> b (4299722064) b -> a (4299722000) b -> c (4299722128) a -> a (4299722000) root -> b (4299722064)
原文 pickle and cPickle – Python object serialization - Python Module of the Week 的後半部分。