Python3 pickle對(duì)象串行化代碼實(shí)例解析
1.pickle對(duì)象串行化
pickle模塊實(shí)現(xiàn)了一個(gè)算法可以將任意的Python對(duì)象轉(zhuǎn)換為一系列字節(jié)。這個(gè)過(guò)程也被稱(chēng)為串行化對(duì)象??梢詡鬏敾虼鎯?chǔ)表示對(duì)象的字節(jié)流,然后再重新構(gòu)造來(lái)創(chuàng)建有相同性質(zhì)的新對(duì)象。
1.1 編碼和解碼字符串中的數(shù)據(jù)
第一個(gè)例子使用dumps()將一個(gè)數(shù)據(jù)結(jié)構(gòu)編碼為一個(gè)字符串,然后把這個(gè)字符串打印到控制臺(tái)。它使用了一個(gè)完全由內(nèi)置類(lèi)型構(gòu)成的數(shù)據(jù)結(jié)構(gòu)。任何類(lèi)的實(shí)例都可以pickled,如后面的例子所示。
import pickle import pprint data = [{'a': 'A', 'b': 2, 'c': 3.0}] print('DATA:', end=' ') pprint.pprint(data) data_string = pickle.dumps(data) print('PICKLE: {!r}'.format(data_string))
默認(rèn)的,pickle將以一種二進(jìn)制格式寫(xiě)入,在Python 3程序之間共享時(shí)這種格式兼容性最好。
數(shù)據(jù)串行化后,可以寫(xiě)到一個(gè)文件、套接字、管道或者其他位置。之后可以讀取這個(gè)文件,將數(shù)據(jù)解除pickled,以便用同樣的值構(gòu)造一個(gè)新對(duì)象。
import pickle import pprint data1 = [{'a': 'A', 'b': 2, 'c': 3.0}] print('BEFORE: ', end=' ') pprint.pprint(data1) data1_string = pickle.dumps(data1) data2 = pickle.loads(data1_string) print('AFTER : ', end=' ') pprint.pprint(data2) print('SAME? :', (data1 is data2)) print('EQUAL?:', (data1 == data2))
新構(gòu)造的對(duì)象等于原來(lái)的對(duì)象,但并不是同一個(gè)對(duì)象。
1.2 處理流
除了dumps()和loads(),pickle還提供了一些便利函數(shù)來(lái)處理類(lèi)似文件的流??梢韵蛞粋€(gè)流寫(xiě)多個(gè)對(duì)象,然后從流讀取這些對(duì)象,而無(wú)須事先知道要寫(xiě)多少個(gè)對(duì)象或者這些對(duì)象多大。
import io import pickle class SimpleObject: def __init__(self, name): self.name = name self.name_backwards = name[::-1] return data = [] data.append(SimpleObject('pickle')) data.append(SimpleObject('preserve')) data.append(SimpleObject('last')) # Simulate a file. out_s = io.BytesIO() # Write to the stream for o in data: print('WRITING : {} ({})'.format(o.name, o.name_backwards)) pickle.dump(o, out_s) out_s.flush() # Set up a read-able stream in_s = io.BytesIO(out_s.getvalue()) # Read the data while True: try: o = pickle.load(in_s) except EOFError: break else: print('READ : {} ({})'.format( o.name, o.name_backwards))
這個(gè)例子使用兩個(gè)BytesIO緩沖區(qū)來(lái)模擬流。第一個(gè)緩沖區(qū)接收pickled的對(duì)象,它的值被填入第二個(gè)緩沖區(qū),load()讀取這個(gè)緩沖區(qū)。簡(jiǎn)單的數(shù)據(jù)庫(kù)格式也可以使用pickle來(lái)存儲(chǔ)對(duì)象。shelve模塊就是這樣一個(gè)實(shí)現(xiàn)。
除了存儲(chǔ)數(shù)據(jù),pickle對(duì)于進(jìn)程間通信也很方便。例如,os.fork()和os.pipe()可以用來(lái)建立工作進(jìn)程,從一個(gè)管道讀取作業(yè)指令,并把結(jié)果寫(xiě)至另一個(gè)管道。管理工作線(xiàn)程池以及發(fā)送作業(yè)和接收響應(yīng)的核心代碼可以重用,因?yàn)樽鳂I(yè)和響應(yīng)對(duì)象不必基于一個(gè)特定的類(lèi)。使用管道或套接字時(shí),在轉(zhuǎn)儲(chǔ)各個(gè)對(duì)象之后不要忘記刷新輸出,以便將數(shù)據(jù)通過(guò)連接推送到另一端。參見(jiàn)multiprocessing模塊來(lái)了解一個(gè)可重用的工作線(xiàn)程池管理器。
1.3 重構(gòu)對(duì)象的問(wèn)題
處理定制類(lèi)時(shí),pickled的類(lèi)必須出現(xiàn)在讀取pickle的進(jìn)程所在的命名空間里。只會(huì)pickled這個(gè)實(shí)例的數(shù)據(jù),而不是類(lèi)定義。類(lèi)名用于查找構(gòu)造函數(shù),以便在解除pickled時(shí)參見(jiàn)新對(duì)象。下面這個(gè)例子將一個(gè)類(lèi)的實(shí)例寫(xiě)至一個(gè)文件。
import pickleclass SimpleObject: def __init__(self, name): self.name = name l = list(name) l.reverse() self.name_backwards = ''.join(l) if __name__ == '__main__': data = [] data.append(SimpleObject('pickle')) data.append(SimpleObject('preserve')) data.append(SimpleObject('last')) with open('Test.py', 'wb') as out_s: for o in data: print('WRITING: {} ({})'.format( o.name, o.name_backwards)) pickle.dump(o, out_s)
運(yùn)行這個(gè)腳本時(shí),會(huì)根據(jù)作為命令行參數(shù)給定的名字來(lái)創(chuàng)建一個(gè)文件。
通過(guò)簡(jiǎn)單的嘗試加載而得到的pickled對(duì)象將會(huì)失敗。
import pickle with open('Test.py', 'rb') as in_s: while True: try: o = pickle.load(in_s) except EOFError: break else: print('READ: {} ({})'.format( o.name, o.name_backwards))
這個(gè)版本失敗的原因在于并沒(méi)有SimpleObject類(lèi)。
修正后的版本從原腳本導(dǎo)入了SimpleObject,這一次運(yùn)行會(huì)成功。在導(dǎo)入列表的最后增加了import語(yǔ)句后,現(xiàn)在腳本就能找到這個(gè)類(lèi)并構(gòu)造對(duì)象了。
from demo import SimpleObject
現(xiàn)在允許修改后的腳本會(huì)生成期望的結(jié)果。
1.4Unpicklable的對(duì)象
并不是所有對(duì)象都是可pickled的。套接字、文件句柄、數(shù)據(jù)庫(kù)連接以及其他運(yùn)行時(shí)狀態(tài)依賴(lài)于操作系統(tǒng)或其他進(jìn)程的對(duì)象,其可能無(wú)法用一種有意義的方式保存。如果對(duì)象包含不可pickled的屬性,則可以定義__getstate__()和__setstate__()來(lái)返回所pickled實(shí)例的狀態(tài)的一個(gè)子集。
__getstate__()方法必須返回一個(gè)對(duì)象,其中包含所pickled對(duì)象的內(nèi)部狀態(tài)。表示狀態(tài)的一種便利方式是使用字典,不過(guò)值可以是任意的可pickled對(duì)象。保存狀態(tài),然后再?gòu)膒ickle加載對(duì)象時(shí)將所保存的狀態(tài)傳入__setstate__()。
import pickle class State: def __init__(self, name): self.name = name def __repr__(self): return 'State({!r})'.format(self.__dict__) class MyClass: def __init__(self, name): print('MyClass.__init__({})'.format(name)) self._set_name(name) def _set_name(self, name): self.name = name self.computed = name[::-1] def __repr__(self): return 'MyClass({!r}) (computed={!r})'.format( self.name, self.computed) def __getstate__(self): state = State(self.name) print('__getstate__ -> {!r}'.format(state)) return state def __setstate__(self, state): print('__setstate__({!r})'.format(state)) self._set_name(state.name) inst = MyClass('name here') print('Before:', inst) dumped = pickle.dumps(inst) reloaded = pickle.loads(dumped) print('After:', reloaded)
這個(gè)例子使用了一個(gè)單獨(dú)的State對(duì)象來(lái)保存MyClass的內(nèi)部狀態(tài)。從pickle加載MyClass的一個(gè)實(shí)例時(shí),會(huì)向__setstate__()傳入一個(gè)State實(shí)例,用來(lái)初始化這個(gè)對(duì)象。
1.5 循環(huán)引用
pickle協(xié)議會(huì)自動(dòng)處理對(duì)象之間的循環(huán)引用,所以復(fù)雜數(shù)據(jù)結(jié)構(gòu)不需要任何特殊的處理。
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)
重新加載的節(jié)點(diǎn)并不是同一個(gè)對(duì)象,但保持了節(jié)點(diǎn)之間的關(guān)系,而且如果對(duì)象有多個(gè)引用,那么只會(huì)重新加載這個(gè)對(duì)象的一個(gè)副本。要驗(yàn)證這兩點(diǎn),可以在通過(guò)pickle傳遞節(jié)點(diǎn)之前和之后檢查節(jié)點(diǎn)的id()值。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Python網(wǎng)絡(luò)編程實(shí)戰(zhàn)之爬蟲(chóng)技術(shù)入門(mén)與實(shí)踐
這篇文章主要介紹了Python網(wǎng)絡(luò)編程實(shí)戰(zhàn)之爬蟲(chóng)技術(shù)入門(mén)與實(shí)踐,了解這些基礎(chǔ)概念和原理將幫助您更好地理解網(wǎng)絡(luò)爬蟲(chóng)的實(shí)現(xiàn)過(guò)程和技巧,需要的朋友可以參考下2023-04-04python ImageDraw類(lèi)實(shí)現(xiàn)幾何圖形的繪制與文字的繪制
這篇文章主要介紹了python ImageDraw類(lèi)實(shí)現(xiàn)幾何圖形的繪制與文字的繪制,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02Python可視化目標(biāo)檢測(cè)框的實(shí)現(xiàn)代碼
這篇文章主要介紹了Python可視化目標(biāo)檢測(cè)框的實(shí)現(xiàn)代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09python實(shí)現(xiàn)讀取并顯示圖片的兩種方法
本篇文章主要介紹python實(shí)現(xiàn)讀取并顯示圖片的兩種方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01可用于監(jiān)控 mysql Master Slave 狀態(tài)的python代碼
用于監(jiān)控MySQL Master Slave 狀態(tài)的python代碼,有需要的朋友可以參考下2013-02-02Python字典循環(huán)添加一鍵多值的用法實(shí)例
今天小編就為大家分享一篇Python字典循環(huán)添加一鍵多值的用法實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01