Python3 pickle對象串行化代碼實例解析
1.pickle對象串行化
pickle模塊實現(xiàn)了一個算法可以將任意的Python對象轉(zhuǎn)換為一系列字節(jié)。這個過程也被稱為串行化對象??梢詡鬏敾虼鎯Ρ硎緦ο蟮淖止?jié)流,然后再重新構(gòu)造來創(chuàng)建有相同性質(zhì)的新對象。
1.1 編碼和解碼字符串中的數(shù)據(jù)
第一個例子使用dumps()將一個數(shù)據(jù)結(jié)構(gòu)編碼為一個字符串,然后把這個字符串打印到控制臺。它使用了一個完全由內(nèi)置類型構(gòu)成的數(shù)據(jù)結(jié)構(gòu)。任何類的實例都可以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)制格式寫入,在Python 3程序之間共享時這種格式兼容性最好。

數(shù)據(jù)串行化后,可以寫到一個文件、套接字、管道或者其他位置。之后可以讀取這個文件,將數(shù)據(jù)解除pickled,以便用同樣的值構(gòu)造一個新對象。
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)造的對象等于原來的對象,但并不是同一個對象。

1.2 處理流
除了dumps()和loads(),pickle還提供了一些便利函數(shù)來處理類似文件的流??梢韵蛞粋€流寫多個對象,然后從流讀取這些對象,而無須事先知道要寫多少個對象或者這些對象多大。
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))
這個例子使用兩個BytesIO緩沖區(qū)來模擬流。第一個緩沖區(qū)接收pickled的對象,它的值被填入第二個緩沖區(qū),load()讀取這個緩沖區(qū)。簡單的數(shù)據(jù)庫格式也可以使用pickle來存儲對象。shelve模塊就是這樣一個實現(xiàn)。

除了存儲數(shù)據(jù),pickle對于進(jìn)程間通信也很方便。例如,os.fork()和os.pipe()可以用來建立工作進(jìn)程,從一個管道讀取作業(yè)指令,并把結(jié)果寫至另一個管道。管理工作線程池以及發(fā)送作業(yè)和接收響應(yīng)的核心代碼可以重用,因為作業(yè)和響應(yīng)對象不必基于一個特定的類。使用管道或套接字時,在轉(zhuǎn)儲各個對象之后不要忘記刷新輸出,以便將數(shù)據(jù)通過連接推送到另一端。參見multiprocessing模塊來了解一個可重用的工作線程池管理器。
1.3 重構(gòu)對象的問題
處理定制類時,pickled的類必須出現(xiàn)在讀取pickle的進(jìn)程所在的命名空間里。只會pickled這個實例的數(shù)據(jù),而不是類定義。類名用于查找構(gòu)造函數(shù),以便在解除pickled時參見新對象。下面這個例子將一個類的實例寫至一個文件。
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)
運行這個腳本時,會根據(jù)作為命令行參數(shù)給定的名字來創(chuàng)建一個文件。

通過簡單的嘗試加載而得到的pickled對象將會失敗。
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))
這個版本失敗的原因在于并沒有SimpleObject類。

修正后的版本從原腳本導(dǎo)入了SimpleObject,這一次運行會成功。在導(dǎo)入列表的最后增加了import語句后,現(xiàn)在腳本就能找到這個類并構(gòu)造對象了。
from demo import SimpleObject
現(xiàn)在允許修改后的腳本會生成期望的結(jié)果。

1.4Unpicklable的對象
并不是所有對象都是可pickled的。套接字、文件句柄、數(shù)據(jù)庫連接以及其他運行時狀態(tài)依賴于操作系統(tǒng)或其他進(jìn)程的對象,其可能無法用一種有意義的方式保存。如果對象包含不可pickled的屬性,則可以定義__getstate__()和__setstate__()來返回所pickled實例的狀態(tài)的一個子集。
__getstate__()方法必須返回一個對象,其中包含所pickled對象的內(nèi)部狀態(tài)。表示狀態(tài)的一種便利方式是使用字典,不過值可以是任意的可pickled對象。保存狀態(tài),然后再從pickle加載對象時將所保存的狀態(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)
這個例子使用了一個單獨的State對象來保存MyClass的內(nèi)部狀態(tài)。從pickle加載MyClass的一個實例時,會向__setstate__()傳入一個State實例,用來初始化這個對象。

1.5 循環(huán)引用
pickle協(xié)議會自動處理對象之間的循環(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é)點并不是同一個對象,但保持了節(jié)點之間的關(guān)系,而且如果對象有多個引用,那么只會重新加載這個對象的一個副本。要驗證這兩點,可以在通過pickle傳遞節(jié)點之前和之后檢查節(jié)點的id()值。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
python 將print輸出的內(nèi)容保存到txt文件中
本文通過代碼給大家介紹了python 將print輸出的內(nèi)容保存到txt文件中,代碼很簡短,需要的朋友可以參考下2018-07-07
python爬蟲爬取淘寶商品比價(附淘寶反爬蟲機制解決小辦法)
這篇文章主要介紹了python爬蟲爬取淘寶商品比價(附淘寶反爬蟲機制解決小辦法),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
Django基礎(chǔ)知識 web框架的本質(zhì)詳解
這篇文章主要介紹了Django基礎(chǔ)知識 web框架的本質(zhì)詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-07-07
Python編程中Python與GIL互斥鎖關(guān)系作用分析
GIL互斥鎖用來保護(hù)Python世界里的對象,防止同一時刻多個線程執(zhí)行Python字節(jié)碼,確保線程安全,但也導(dǎo)致Python線程無法利用多核CPU優(yōu)勢,本文來探討Python將來是否有可能去除GIL2021-09-09
詳解基于Transformer實現(xiàn)電影評論星級分類任務(wù)
這篇文章主要為大家介紹了詳解基于Transformer實現(xiàn)電影評論星級分類任務(wù)過程解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
Python實現(xiàn)農(nóng)歷轉(zhuǎn)換教程詳解
農(nóng)歷,是我國現(xiàn)行的傳統(tǒng)歷法。它是根據(jù)月相的變化周期,每一次月相朔望變化為一個月,參考太陽回歸年為一年的長度,并加入二十四節(jié)氣與設(shè)置閏月以使平均歷年與回歸年相適應(yīng)。本文將用Python實現(xiàn)農(nóng)歷轉(zhuǎn)換,需要的可以參考一下2022-03-03

