目的:Python物件序列化
可用性:pickle至少1.4版本,cPickle 1.5版本以上
pickle
模組實現了一種演算法,將任意一個Python物件轉化成一系列位元組(byets)。此過程也呼叫了serializing
物件。代表物件的位元組流之後可以被傳輸或儲存,再重構後建立一個擁有相同特徵(the same characteristics)的新的物件。
cPickle
使用C而不是Python,實現了相同的演算法。這比Python實現要快好幾倍,但是它不允許使用者從Pickle派生子類。如果子類對你的使用來說無關緊要,那麼cPickle是個更好的選擇。
警告:本文件直接說明,pickle不提供安全保證。如果你在多執行緒通訊(inter-process communication)或者資料儲存或儲存資料中使用pickle,一定要小心。請勿信任你不能確定為安全的資料。
匯入
如平常一樣,嘗試匯入cPickle,給它賦予一個別名“pickle”。如果因為某些原因匯入失敗,退而求其次到Python的原生(native)實現pickle模組。如果cPickle可用,能給你提供一個更快速的執行,否則只能是輕便的執行(the portable implementation)。
1 2 3 4 5 |
try: import cPickle as pickle except: import pickle |
編碼和解碼
第一個例子將一種資料結構編碼成一個字串,然後把該字串列印至控制檯。使用一種包含所有原生型別(native types)的資料結構。任何型別的例項都可被醃漬(pickled,譯者注:模組名稱pickle的中文含義為醃菜),在稍後的例子中會演示。使用pickle.dumps()
來建立一個表示該物件值的字串。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
try: import cPickle as pickle except: import pickle import pprint data = [ { 'a':'A', 'b':2, 'c':3.0 } ] print 'DATA:', pprint.pprint(data) data_string = pickle.dumps(data) print 'PICKLE:', data_string |
pickle預設僅由ASCII字元組成。也可以使用更高效的二進位制格式(binary format),只是因為在列印的時候更易於理解,本頁的所有例子都使用ASCII輸出。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ python pickle_string.py DATA:[{'a': 'A', 'b': 2, 'c': 3.0}] PICKLE: (lp1 (dp2 S'a' S'A' sS'c' F3 sS'b' I2 sa. |
資料被序列化以後,你可以將它們寫入檔案、套接字、管道等等中。之後你也可以從檔案中讀取出來、將它反醃漬(unpickled)而構造一個具有相同值得新物件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
try: import cPickle as pickle except: import pickle import pprint data1 = [ { 'a':'A', 'b':2, 'c':3.0 } ] print 'BEFORE:', pprint.pprint(data1) data1_string = pickle.dumps(data1) data2 = pickle.loads(data1_string) print 'AFTER:', pprint.pprint(data2) print 'SAME?:', (data1 is data2) print 'EQUAL?:', (data1 == data2) |
如你所見,這個新構造的物件與原物件相同,但並非同一物件。這不足為奇。
1 2 3 4 5 6 7 |
$ python pickle_unpickle.py BEFORE:[{'a': 'A', 'b': 2, 'c': 3.0}] AFTER:[{'a': 'A', 'b': 2, 'c': 3.0}] SAME?: False EQUAL?: True |
與流一起工作
除dumps()
和loads()
外,pickle還提供一對用在類檔案流(file-like streams)的轉化函式。可以往一個流中寫對個物件,然後從流中把它們讀取出來,此過程不需要預先寫入的物件有幾個、它們多大。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
try: import cPickle as pickle except: import pickle import pprint from StringIO import StringIO class SimpleObject(object): def __init__(self, name): self.name = name l = list(name) l.reverse() self.name_backwards = ''.join(l) return data = [] data.append(SimpleObject('pickle')) data.append(SimpleObject('cPickle')) data.append(SimpleObject('last')) # 使用StringIO模擬一個檔案 out_s = StringIO() # 寫入該流 for o in data: print 'WRITING: %s (%s)' % (o.name, o.name_backwards) pickle.dump(o, out_s) out_s.flush() # 建立一個可讀流 in_s = StringIO(out_s.getvalue()) # 讀資料 while True: try: o = pickle.load(in_s) except EOFError: break else: print 'READ: %s (%s)' % (o.name, o.name_backwards) |
這個例子使用SringIO快取器(buffer)模擬流,所以在建立可讀流的時候我們玩了一把。一個簡單資料庫的格式化也可以使用pickles來儲存物件,只是shelve
與之工作更加簡便。
1 2 3 4 5 6 7 8 9 |
$ python pickle_stream.py WRITING: pickle (elkcip) WRITING: cPickle (elkciPc) WRITING: last (tsal) READ: pickle (elkcip) READ: cPickle (elkciPc) READ: last (tsal) |
除了儲存資料,pickles在程式間通訊(inter-process communication)中也非常稱手。例如,使用os.fork()
和os.pipe()
可以創立工作者程式(worker processes),從一個管道(pipe)讀取作業指令(job instruction)然後將結果寫入另一個管道。管理工作者池(worker pool)和將作業送入、接受響應(response)的核心程式碼可被重用,因為作業和響應並不屬於某個特定類中。如果你使用管道或者套接字(sockets),在通過連至另一端(end)的連線傾倒(dumps)所有物件、推送資料之後,別忘了沖洗(flush)。如果你想寫自己的工作者池管理器,請看multiprocessing
。