pickle和cPickle:Python對象的序列化(下)

承接上文 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

環形引用(Circular References)

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 的後半部分。

相關文章
相關標籤/搜索