做(zuò)自(zì)由與創造的先行(xíng)者

數(shù)據持久性 | Data Persistence pickle

Python開(kāi)發手冊

該pickle模塊為(wèi)序列化和(hé)反序列化Python對象結構實現了(le)一(yī)個基本但(dàn)強大的算法。“Pickling”是将Python對象層次結構轉換為(wèi)字節流的過程,“unpickling”是相反的操作(zuò),即字節流轉換回對象層次結構。Pickling(或取消)也被稱為(wèi)“序列化”,“編組”,或“扁平化”,但(dàn)是,為(wèi)避免混淆,這(zhè)裏使用的術(shù)語是“酸洗”和(hé)“取消”。

本文檔描述了(le)pickle模塊和(hé)cPickle模塊。

警告

該pickle模塊對于錯誤或惡意構建的數(shù)據不安全。切勿取消從(cóng)不可(kě)信或未經認證的來源收到的數(shù)據。

1.與其他(tā)Python模塊的關系

該pickle模塊有一(yī)個稱為(wèi)cPickle模塊的優化堂兄。顧名思義,cPickle就是用C編寫的,所以它的速度可(kě)以比C快(kuài)1000倍pickle。但(dàn)是它不支持Pickler()和(hé)Unpickler()類的子類化,因為(wèi)在cPickle這(zhè)些函數(shù)中,不是類。大多數(shù)應用程序不需要(yào)此功能(néng),并且可(kě)以從(cóng)改進的性能(néng)中受益cPickle。除此之外(wài),兩個模塊的接口幾乎完全相同; 本手冊介紹了(le)通(tōng)用接口,并在必要(yào)時(shí)指出了(le)不同之處。在下(xià)面的讨論中,我們使用術(shù)語“泡菜”共同描述pickle和(hé)cPickle模塊。

兩個模塊産生的數(shù)據流保證可(kě)以互換。

Python有一(yī)個更原始的序列化模塊marshal,但(dàn)通(tōng)常pickle應該是序列化Python對象的首選方式。marshal主要(yào)是為(wèi)了(le)支持Python的.pyc文件。

該pickle模塊與以下(xià)marshal幾個重要(yào)方面有所不同:

該pickle模塊跟蹤它已經序列化的對象,以便以後對同一(yī)對象的引用不會再次序列化。marshal不這(zhè)樣做(zuò)。這(zhè)對遞歸對象和(hé)對象共享都(dōu)有影響。遞歸對象是包含對自(zì)己的引用的對象。這(zhè)些不是由編組處理的,實際上(shàng),嘗試編組遞歸對象會導緻Python解釋器崩潰。如(rú)果在被序列化的對象層次結構中的不同位置存在對同一(yī)對象的多個引用,則會發生對象共享。pickle隻存儲一(yī)次這(zhè)樣的對象,并确保所有其他(tā)引用指向主副本。共享對象保持共享,這(zhè)對于可(kě)變對象非常重要(yào)。

marshal不能(néng)用于序列化用戶定義的類及其實例。pickle可(kě)以透明(míng)地(dì)保存和(hé)恢複類實例,但(dàn)類定義必須是可(kě)導入的,并且與存儲對象時(shí)位于同一(yī)模塊中。

該marshal序列化格式是不能(néng)保證整個Python版本移植。因為(wèi)它的主要(yào)工(gōng)作(zuò)是支持.pyc文件,所以Python實現者保留在需要(yào)時(shí)以非向後兼容方式更改序列化格式的權利。該pickle序列化格式是保證不同的Python版本向後兼容。

請(qǐng)注意,序列化是比持久性更原始的概念; 雖然pickle讀取和(hé)寫入文件對象,但(dàn)它不處理命名持久對象的問(wèn)題,也不處理并發訪問(wèn)持久對象的(更複雜(zá)的)問(wèn)題。該pickle模塊可(kě)以将複雜(zá)對象轉換為(wèi)字節流,并且可(kě)以将字節流轉換為(wèi)具有相同內(nèi)部結構的對象。也許對這(zhè)些字節流最明(míng)顯的做(zuò)法是将它們寫入文件,但(dàn)也可(kě)以将它們發送到網絡或将它們存儲在數(shù)據庫中。該模塊shelve提供了(le)一(yī)個簡單的界面,可(kě)以在DBM樣式的數(shù)據庫文件上(shàng)腌制和(hé)取消對象。

2.數(shù)據流格式

所使用的數(shù)據格式pickle是Python特有的。這(zhè)具有如(rú)下(xià)優點:不存在由諸如(rú)XDR的外(wài)部标準(其不能(néng)表示指針共享)施加的限制; 然而這(zhè)意味着非Python程序可(kě)能(néng)無法重構pickled Python對象。

默認情況下(xià),pickle數(shù)據格式使用可(kě)打印的ASCII表示。這(zhè)比二進制表示稍大一(yī)些。使用可(kě)打印ASCII(以及其他(tā)pickle表示形式的其他(tā)特征)的一(yī)大優點是,出于調試或恢複的目的,人(rén)們可(kě)以使用标準文本編輯器閱讀腌制文件。

目前有3種不同的協議(yì)可(kě)用于pickling。

協議(yì)版本0是原始的ASCII協議(yì),并且與早期版本的Python向後兼容。

協議(yì)版本1是舊(jiù)的二進制格式,它也與早期版本的Python兼容。

協議(yì)版本2是在Python 2.3中引入的。它提供了(le)更有效的酸洗新式課程。

有關更多信息,請(qǐng)參閱PEP 307。

如(rú)果一(yī)個協議(yì)沒有指定,協議(yì)0被使用。如(rú)果協議(yì)被指定為(wèi)負值,或HIGHEST_PROTOCOL将使用可(kě)用的最高協議(yì)版本。

版本2.3中更改:引入了(le)協議(yì)參數(shù)。

可(kě)以通(tōng)過指定協議(yì)版本> = 1 來選擇稍微(wēi)更高效的二進制格式。

3.用法

要(yào)序列化對象層次結構,首先創建一(yī)個pickler,然後調用pickler的dump()方法。為(wèi)了(le)反序列化數(shù)據流,首先創建一(yī)個unpickler,然後調用unpickler的load()方法。該pickle模塊提供以下(xià)常數(shù):

pickle.HIGHEST_PROTOCOL

可(kě)用的最高協議(yì)版本。該值可(kě)以作(zuò)為(wèi)協議(yì)值傳遞。

2.3版本的新功能(néng)。

注意

确保始終以二進制模式打開(kāi)使用協議(yì)> = 1創建的pickle文件。對于舊(jiù)的基于ASCII的pickle協議(yì)0,隻要(yào)保持一(yī)緻,就可(kě)以使用文本模式或二進制模式。

在二進制模式下(xià)使用協議(yì)0編寫的pickle文件将包含單行(xíng)換行(xíng)符作(zuò)為(wèi)行(xíng)終止符,因此在使用記事本或其他(tā)不支持此格式的編輯器中查看(kàn)時(shí)看(kàn)起來會很(hěn)“滑稽”。

該pickle模塊提供以下(xià)功能(néng),使酸洗過程更加方便:

pickle.dump(obj, file[, protocol])

将obj的pickle表示寫入打開(kāi)的文件對象文件。這(zhè)相當于Pickler(file, protocol).dump(obj)。

如(rú)果協議(yì)參數(shù)被省略,則使用協議(yì)0。如(rú)果協議(yì)被指定為(wèi)負值,或者HIGHEST_PROTOCOL将使用最高協議(yì)版本。

版本2.3中更改:引入了(le)協議(yì)參數(shù)。

文件必須有一(yī)個write()接受單個字符串參數(shù)的方法。因此它可(kě)以是一(yī)個為(wèi)寫入而打開(kāi)的文件對象,一(yī)個StringIO對象或符合此接口的任何其他(tā)自(zì)定義對象。

pickle.load(file)

從(cóng)打開(kāi)的文件對象文件中讀取一(yī)個字符串,并将其解釋為(wèi)pickle數(shù)據流,重建并返回原始對象層次結構。這(zhè)相當于Unpickler(file).load()。

文件必須有兩個方法,一(yī)個read()采用整數(shù)參數(shù)的readline()方法和(hé)一(yī)個不需要(yào)參數(shù)的方法。兩種方法都(dōu)應該返回一(yī)個字符串 因此,文件可(kě)以是為(wèi)閱讀而打開(kāi)的文件對象,StringIO對象或符合此界面的任何其他(tā)自(zì)定義對象。

該功能(néng)自(zì)動确定數(shù)據流是否以二進制模式寫入。

pickle.dumps(obj[, protocol])

将對象的pickled表示形式返回為(wèi)字符串,而不是将其寫入文件。

如(rú)果協議(yì)參數(shù)被省略,則使用協議(yì)0。如(rú)果協議(yì)被指定為(wèi)負值,或者HIGHEST_PROTOCOL将使用最高協議(yì)版本。

在版本2.3中更改:添加了(le)協議(yì)參數(shù)。

pickle.loads(string)

從(cóng)字符串中讀取一(yī)個pickled對象層次結構。字符串中超過pickle對象表示的字符将被忽略。

該pickle模塊還定義了(le)三個例外(wài):

exception pickle.PickleError

下(xià)面定義的其他(tā)例外(wài)的通(tōng)用基類。這(zhè)繼承了(le)Exception。

exception pickle.PicklingError

當不可(kě)識别的對象傳遞給dump()方法時(shí)引發此異常。

exception pickle.UnpicklingError

當取消對象時(shí)出現問(wèn)題時(shí)會引發此異常。需要(yào)注意的是其他(tā)異常也可(kě)以取儲存,包括(但(dàn)不一(yī)定局限于)過程中引發的AttributeError,EOFError,ImportError,和(hé)IndexError。

該pickle模塊還導出兩個可(kě)調用的[2],Pickler并且Unpickler:

class pickle.Pickler(file[, protocol])

這(zhè)需要(yào)一(yī)個文件類對象,它将寫入一(yī)個pickle數(shù)據流。

如(rú)果協議(yì)參數(shù)被省略,則使用協議(yì)0。如(rú)果協議(yì)被指定為(wèi)負值,或者HIGHEST_PROTOCOL将使用最高協議(yì)版本。

版本2.3中更改:引入了(le)協議(yì)參數(shù)。

文件必須有一(yī)個write()接受單個字符串參數(shù)的方法。因此,它可(kě)以是一(yī)個打開(kāi)的文件對象,一(yī)個StringIO對象或符合此接口的任何其他(tā)自(zì)定義對象。

Pickler 對象定義一(yī)個(或兩個)公共方法:

dump(obj)

向構造函數(shù)中給出的打開(kāi)的文件對象寫一(yī)個腌制的obj表示形式。将使用二進制或ASCII格式,具體取決于傳遞給構造函數(shù)的協議(yì)參數(shù)的值。

clear_memo()

清除pickler的“備忘錄”。備忘錄是記錄pickler已經看(kàn)到的對象的數(shù)據結構,以便共享或遞歸對象通(tōng)過引用而不是按值進行(xíng)pickle。這(zhè)種方法在重新使用pickler時(shí)很(hěn)有用。

注意

在Python 2.3之前,clear_memo()僅在創建的picker上(shàng)可(kě)用cPickle。在pickle模塊中,picklers有一(yī)個實例變量叫做(zuò)memowhich是一(yī)個Python字典。因此,要(yào)清除pickle模塊拾取器的備忘錄,您可(kě)以執行(xíng)以下(xià)操作(zuò):

mypickler.memo.clear()

複制

不需要(yào)支持較舊(jiù)版本的Python的代碼應該簡單地(dì)使用clear_memo()。

可(kě)以對dump()同一(yī)個Pickler實例的方法進行(xíng)多次調用。然後這(zhè)些必須匹配到load()相應Unpickler實例的方法的相同數(shù)量的調用。如(rú)果同一(yī)個對象被多次dump()調用腌制,那(nà)麽這(zhè)個load()将全部産生對同一(yī)個對象的引用。[3]

Unpickler 對象被定義為(wèi):

class pickle.Unpickler(file)

這(zhè)需要(yào)一(yī)個類似文件的對象,它将從(cóng)中讀取一(yī)個pickle數(shù)據流。該類自(zì)動确定數(shù)據流是否以二進制模式寫入,因此它不需要(yào)Pickler工(gōng)廠中的标志。

文件必須有兩個方法,一(yī)個read()采用整數(shù)參數(shù)的readline()方法和(hé)一(yī)個不需要(yào)參數(shù)的方法。兩種方法都(dōu)應該返回一(yī)個字符串 因此,文件可(kě)以是為(wèi)閱讀而打開(kāi)的文件對象,StringIO對象或符合此界面的任何其他(tā)自(zì)定義對象。

Unpickler 對象有一(yī)個(或兩個)公共方法:

load()

從(cóng)構造函數(shù)中給出的打開(kāi)文件對象中讀取一(yī)個pickle對象表示形式,并返回其中指定的重構對象層次結構。

該方法自(zì)動确定數(shù)據流是否以二進制模式寫入。

noload()

這(zhè)就像load()除了(le)它實際上(shàng)不創建任何對象。這(zhè)主要(yào)用于查找可(kě)能(néng)在pickle數(shù)據流中引用的稱為(wèi)“持久性id”的東西(xī)。有關更多詳細信息,請(qǐng)參見(jiàn)下(xià)面的pickle協議(yì)。

注意:該noload()方法當前僅Unpickler在使用該cPickle模塊創建的對象上(shàng)可(kě)用。pickle模塊Unpickler沒有這(zhè)個noload()方法。

4.什麽可(kě)以 pickled和(hé)unpickled?

以下(xià)類型可(kě)以被pickled:

None, True, and False

整數(shù),長(cháng)整數(shù),浮點數(shù),複數(shù)

正常和(hé)Unicode字符串

元組,列表,集合和(hé)僅包含可(kě)選對象的字典

函數(shù)定義在模塊的頂層

在模塊頂層定義的內(nèi)置函數(shù)

在模塊頂層定義的類

這(zhè)些類的實例__dict__或調用的結果__getstate__()是可(kě)挑選的(請(qǐng)參閱pickle協議(yì)的細節部分)。

嘗試pickle unpicklable對象會引發PicklingError異常; 發生這(zhè)種情況時(shí),可(kě)能(néng)已将未指定數(shù)量的字節寫入底層文件。試圖腌制一(yī)個高度遞歸的數(shù)據結構可(kě)能(néng)會超過最大遞歸深度,RuntimeError在這(zhè)種情況下(xià)會引發一(yī)次。你可(kě)以謹慎地(dì)提高這(zhè)個限制sys.setrecursionlimit()。

請(qǐng)注意,函數(shù)(內(nèi)置的和(hé)用戶定義的)由“完全限定”名稱引用進行(xíng)挑選,而不是按值進行(xíng)。這(zhè)意味着隻有函數(shù)名稱被腌漬,以及定義該函數(shù)的模塊的名稱。該函數(shù)的代碼及其任何函數(shù)屬性都(dōu)不會被腌制。因此,定義模塊必須可(kě)以在取消環境中導入,并且模塊必須包含指定的對象,否則将引發異常。[4]

同樣,類按名稱引用進行(xíng)挑選,因此在取消環境中适用相同的限制。請(qǐng)注意,沒有任何類的代碼或數(shù)據被腌制,因此在下(xià)面的示例中,attr不會在unpickling環境中恢複class屬性:

class Foo:

attr = 'a class attr'

picklestring = pickle.dumps(Foo)

複制

這(zhè)些限制是為(wèi)什麽必須在模塊的頂層定義可(kě)調用的函數(shù)和(hé)類。

同樣,當類實例被腌制時(shí),他(tā)們的類的代碼和(hé)數(shù)據不會随着它們一(yī)起被腌制。隻有實例數(shù)據被腌制。這(zhè)是有意完成的,因此您可(kě)以修複類中的錯誤或向類中添加方法,并仍然加載使用該類的早期版本創建的對象。如(rú)果您計劃使用能(néng)夠看(kàn)到許多版本的類的長(cháng)效對象,則可(kě)能(néng)需要(yào)在對象中添加版本号,以便可(kě)以通(tōng)過類的__setstate__()方法進行(xíng)适當的轉換。

5.pickle協議(yì)

本節介紹定義Pickler / unpickler和(hé)正在序列化的對象之間(jiān)接口的“酸洗協議(yì)”。該協議(yì)為(wèi)您定義,定制和(hé)控制對象如(rú)何序列化和(hé)反序列化提供了(le)一(yī)種标準方法。本節中的描述不包括您可(kě)以使用的特定自(zì)定義設置,以使不受信任的pickle數(shù)據流更安全一(yī)些。有關更多詳細信息,請(qǐng)參見(jiàn)子類化Unpicklers部分。

5.1.酸洗和(hé)取消正常的類實例

object.__getinitargs__()

當pickled類實例被取消選中時(shí),__init__()通(tōng)常不調用它的方法。如(rú)果需要(yào)在__init__()取消打開(kāi)時(shí)調用該方法,則舊(jiù)式類可(kě)以定義一(yī)個方法__getinitargs__(),該方法應返回包含要(yào)傳遞給類構造函數(shù)的參數(shù)的元組(__init__()例如(rú))。該__getinitargs__()方法在腌制時(shí)間(jiān)被調用; 它返回的元組被包含在實例的pickle中。

object.__getnewargs__()

新樣式類型可(kě)以提供__getnewargs__()用于協議(yì)2的方法。如(rú)果類型在創建實例時(shí)建立了(le)一(yī)些內(nèi)部不變量,或者如(rú)果內(nèi)存分配受到傳遞給__new__()該類型方法的值的影響,則需要(yào)實現此方法(因為(wèi)它是元組和(hé)字符串)。新風(fēng)格類的 實例C是使用創建的

obj = C.__new__(C, *args)

複制

其中ARGS是調用的結果而__getnewargs__()原來的對象上(shàng); 如(rú)果不存在__getnewargs__(),則假定一(yī)個空元組。

object.__getstate__()

課程可(kě)以進一(yī)步影響他(tā)們的實例如(rú)何腌制; 如(rú)果類定義了(le)該方法__getstate__(),則會調用該方法,并将返回狀态作(zuò)為(wèi)實例的內(nèi)容進行(xíng)挑選,而不是實例字典的內(nèi)容。如(rú)果沒有__getstate__()方法,則實例__dict__被腌制。

object.__setstate__(state)

取消之後,如(rú)果類也定義了(le)該方法__setstate__(),那(nà)麽将使用unpickled狀态調用該方法。[5]如(rú)果沒有__setstate__()方法,pickled狀态必須是一(yī)個字典,并且它的項目被分配給新實例的字典。如(rú)果一(yī)個類定義了(le)__getstate__()和(hé)__setstate__(),狀态對象不一(yī)定是字典,這(zhè)些方法可(kě)以做(zuò)他(tā)們想要(yào)的東西(xī)。[6]

Note

對于新樣式類,如(rú)果__getstate__()返回一(yī)個假值,則該__setstate__()方法不會被調用。

注意

在在unpickle時(shí),一(yī)些方法,如(rú)__getattr__(),__getattribute__()或__setattr__()可(kě)在該實例調用。如(rú)果這(zhè)些方法依賴于一(yī)些內(nèi)部不變為(wèi)真,則類型應該實現任一(yī)__getinitargs__()或__getnewargs__()建立這(zhè)樣的不變的; 否則,既__new__()不會也__init__()不會被調用。

5.2.酸洗和(hé)取消擴展類型

object.__reduce__()

當Pickler遇到一(yī)個類型的對象時(shí),它一(yī)無所知 - 例如(rú)擴展類型 - 它在兩個地(dì)方尋找如(rú)何腌制它的提示。一(yī)種替代方案是對象實現一(yī)種__reduce__()方法。如(rú)果提供,在酸洗時(shí)__reduce__()将被調用,不帶任何參數(shù),并且它必須返回一(yī)個字符串或一(yī)個元組。

如(rú)果返回一(yī)個字符串,它就會命名一(yī)個全局變量,其內(nèi)容被正常腌制。返回的字符串__reduce__()應該是相對于其模塊的對象的本地(dì)名稱; pickle模塊搜索模塊名稱空間(jiān)以确定對象的模塊。

當一(yī)個元組返回時(shí),它的長(cháng)度必須在2到5個元素之間(jiān)。可(kě)選元素可(kě)以省略,None也可(kě)以作(zuò)為(wèi)它們的值提供。這(zhè)個元組的內(nèi)容按照正常方式進行(xíng)腌制,并且在取出時(shí)用于重建對象。每個元素的語義是:

可(kě)調用的對象,将被調用來創建該對象的初始版本。元組的下(xià)一(yī)個元素将為(wèi)此可(kě)調用對象提供參數(shù),随後的元素将提供附加的狀态信息,随後将用它們來完全重構pickle數(shù)據。在unpickling環境中,這(zhè)個對象必須是一(yī)個類,一(yī)個可(kě)調用的注冊為(wèi)“安全構造函數(shù)”(參見(jiàn)下(xià)文),或者它必須具有__safe_for_unpickling__一(yī)個真值的屬性。否則,UnpicklingError将在未開(kāi)封的環境中提出。請(qǐng)注意,像往常一(yī)樣,可(kě)調用本身是按名稱腌制的。

可(kě)調用對象的參數(shù)元組。

在版本2.5中改變了(le):以前,這(zhè)個論點也可(kě)以None。

可(kě)選地(dì),該對象的狀态将按照__setstate__()Pickling和(hé)Unickling普通(tōng)類實例中所述傳遞給對象的方法。如(rú)果該對象沒有__setstate__()方法,那(nà)麽,如(rú)上(shàng)所述,該值必須是一(yī)個字典,它将被添加到該對象的__dict__。

可(kě)選地(dì),叠代器(而不是序列)産生連續的列表項。這(zhè)些列表項将被酸洗,并追加到使用任一(yī)對象obj.append(item)或obj.extend(list_of_items)。這(zhè)主要(yào)用于列表子類,但(dàn)可(kě)以由其他(tā)類使用,隻要(yào)它們具有相應的簽名append()并且extend()具有适當的簽名方法。(無論append()或extend()使用取決于哪泡菜協議(yì)版本被用作(zuò)以及項目追加的次數(shù),所以兩者都(dōu)必須被支持。)

可(kě)選地(dì),一(yī)個叠代器(而不是一(yī)個序列)産生連續的字典項目,它們應該是表單的元組(key, value)。這(zhè)些項目将被酸洗并存儲到對象使用obj[key] = value。這(zhè)主要(yào)用于字典子類,但(dàn)隻要(yào)它們實現,可(kě)以由其他(tā)類使用__setitem__()。

object.__reduce_ex__(protocol)

在實施時(shí)了(le)解協議(yì)版本有時(shí)很(hěn)有用__reduce__()。這(zhè)可(kě)以通(tōng)過實現一(yī)個名為(wèi),__reduce_ex__()而不是__reduce__()。__reduce_ex__(),當它存在時(shí),被優先調用__reduce__()(你仍然可(kě)以提供__reduce__()向後兼容性)。該__reduce_ex__()方法将使用單個整數(shù)參數(shù)(協議(yì)版本)進行(xíng)調用。

這(zhè)個object類實現了(le)__reduce__()和(hé)__reduce_ex__(); 然而,如(rú)果一(yī)個子類覆蓋__reduce__()但(dàn)不是__reduce_ex__(),__reduce_ex__()實現檢測到這(zhè)一(yī)點并調用__reduce__()。

__reduce__()在要(yào)被腌制的對象上(shàng)實現方法的另一(yī)種方法是向copy_reg模塊注冊可(kě)調用對象。該模塊為(wèi)程序提供了(le)一(yī)種注冊用戶定義類型的“簡化函數(shù)”和(hé)構造函數(shù)的方法。約簡函數(shù)具有與上(shàng)述__reduce__()方法相同的語義和(hé)接口,隻不過它們是用一(yī)個參數(shù)調用的,這(zhè)個對象是被腌制的。

如(rú)上(shàng)所述,已注冊的構造函數(shù)被視(shì)為(wèi)“安全構造函數(shù)”,用于拆除目的。

5.3.酸洗和(hé)取出外(wài)部物(wù)體

為(wèi)了(le)獲得對象持久性,pickle模塊支持對pickle數(shù)據流之外(wài)的對象的引用的概念。這(zhè)些對象由“持久性id”引用,它隻是可(kě)打印的ASCII字符的任意字符串。這(zhè)些名稱的解析不是由pickle模塊定義的; 它将把這(zhè)個分辨率委托給pickler和(hé)unpickler上(shàng)的用戶定義函數(shù)。[7]

要(yào)定義外(wài)部持久性标識解析,您需要(yào)設置persistent_idpickler對象的persistent_load屬性和(hé)unpickler對象的屬性。

要(yào)pickle具有外(wài)部持久性id的對象,picker必須有一(yī)個自(zì)定義persistent_id()方法,它将一(yī)個對象作(zuò)為(wèi)參數(shù),并返回None該對象的持久性id或該持久性id。當None返回時(shí),隻需皮克勒泡菜對象為(wèi)正常。當返回一(yī)個持久化的id字符串時(shí),pickler會腌制該字符串以及一(yī)個标記,這(zhè)樣unpickler會将該字符串識别為(wèi)持久性id。

要(yào)取消對外(wài)部對象的打擊,unpickler必須具有一(yī)個自(zì)定義persistent_load()函數(shù),該函數(shù)采用持久性id字符串并返回引用的對象。

這(zhè)是一(yī)個愚蠢的例子,可(kě)能(néng)會提供更多的信息:

import pickle

from cStringIO import StringIO

src = StringIO()

p = pickle.Pickler(src)

def persistent_id(obj):

if hasattr(obj, 'x'):

return 'the value %d' % obj.x

else:

return None

p.persistent_id = persistent_id

class Integer:

def __init__(self, x):

self.x = x

def __str__(self):

return 'My name is integer %d' % self.x

i = Integer(7)

print i

p.dump(i)

datastream = src.getvalue()

print repr(datastream)

dst = StringIO(datastream)

up = pickle.Unpickler(dst)

class FancyInteger(Integer):

def __str__(self):

return 'I am the integer %d' % self.x

def persistent_load(persid):

if persid.startswith('the value '):

value = int(persid.split()[2])

return FancyInteger(value)

else:

raise pickle.UnpicklingError, 'Invalid persistent id'

up.persistent_load = persistent_load

j = up.load()

print j

複制

在cPickle模塊中,unpickler的persistent_load屬性也可(kě)以設置為(wèi)一(yī)個Python列表,在這(zhè)種情況下(xià),當unpickler到達一(yī)個持久id時(shí),持久id字符串将被簡單地(dì)附加到這(zhè)個列表中。這(zhè)個功能(néng)的存在使得pickle數(shù)據流可(kě)以被“嗅探”而不需要(yào)真正實例化pickle中的所有對象。[8]設置persistent_load為(wèi)列表通(tōng)常與noload()Unpickler上(shàng)的方法一(yī)起使用。

6.子類化Unpicklers

默認情況下(xià),unpickling會導入它在pickle數(shù)據中找到的任何類。您可(kě)以準确地(dì)控制取消撥号的內(nèi)容以及通(tōng)過自(zì)定義取消撥号程序調用的內(nèi)容。不幸的是,你究竟如(rú)何做(zuò)到這(zhè)一(yī)點,取決于你是使用pickle還是不同cPickle。[9]

在pickle模塊中,您需要(yào)派生一(yī)個子類Unpickler,覆蓋該load_global()方法。load_global()應該從(cóng)pickle數(shù)據流中讀取兩行(xíng),其中第一(yī)行(xíng)是包含類的模塊的名稱,第二行(xíng)是實例類的名稱。然後它查找類,可(kě)能(néng)導入模塊并挖掘屬性,然後将它找到的內(nèi)容追加到unpickler的堆棧中。之後,這(zhè)個類将被分配給__class__一(yī)個空類的屬性,作(zuò)為(wèi)魔術(shù)般創建一(yī)個實例而不調用它的類的一(yī)種方式__init__()。你的工(gōng)作(zuò)(如(rú)果你選擇接受它)将是有的load_global()推到unpickler的堆棧,一(yī)個已知的安全版本的任何你認為(wèi)可(kě)以安全取出的類。由你來制作(zuò)這(zhè)樣的課程。或者,如(rú)果您想禁止所有取消打開(kāi)實例,則可(kě)能(néng)會出現錯誤。如(rú)果這(zhè)聽起來像一(yī)個黑(hēi)客,你說得對。參考源代碼來完成這(zhè)項工(gōng)作(zuò)。

事情有點清潔cPickle,但(dàn)不是太多。要(yào)控制取消選中的對象,可(kě)以将unpickler的find_global屬性設置為(wèi)一(yī)個函數(shù)或None。如(rú)果是的None話,任何試圖解除實例的嘗試都(dōu)會引發一(yī)次UnpicklingError。如(rú)果它是一(yī)個函數(shù),那(nà)麽它應該接受一(yī)個模塊名稱和(hé)一(yī)個類名,并返回相應的類對象。它負責查找課程并執行(xíng)任何必要(yào)的導入操作(zuò),并且可(kě)能(néng)會引發錯誤,以防止課堂實例被取消。

這(zhè)個故事的寓意是你應該非常小心你的應用程序取消選擇的字符串的來源。

7.例子

對于最簡單的代碼,使用dump()和(hé)load()函數(shù)。請(qǐng)注意,自(zì)引用列表已被酸洗并正确恢複。

import pickle

data1 = {'a': [1, 2.0, 3, 4+6j],

'b': ('string', u'Unicode string'),

'c': None}

selfref_list = [1, 2, 3]

selfref_list.append(selfref_list)

output = open('data.pkl', 'wb')

# Pickle dictionary using protocol 0.

pickle.dump(data1, output)

# Pickle the list using the highest protocol available.

pickle.dump(selfref_list, output, -1)

output.close()

複制

以下(xià)示例讀取所産生的腌制數(shù)據。當讀取含有腌菜的文件時(shí),應該以二進制模式打開(kāi)文件,因為(wèi)您無法确定是否使用了(le)ASCII或二進制格式。

import pprint, pickle

pkl_file = open('data.pkl', 'rb')

data1 = pickle.load(pkl_file)

pprint.pprint(data1)

data2 = pickle.load(pkl_file)

pprint.pprint(data2)

pkl_file.close()

複制

下(xià)面是一(yī)個更大的例子,展示了(le)如(rú)何修改一(yī)個類的酸洗行(xíng)為(wèi)。本TextReader類打開(kāi)一(yī)個文本文件,并返回每一(yī)次它的行(xíng)号和(hé)行(xíng)內(nèi)容,readline()方法被調用。如(rú)果一(yī)個TextReader實例被腌制,除文件對象成員(yuán)之外(wài)的所有屬性都(dōu)将被保存。當實例取消選中時(shí),将重新打開(kāi)該文件,并從(cóng)最後一(yī)個位置繼續讀取。該__setstate__()和(hé)__getstate__()方法來實現此行(xíng)為(wèi)。

#!/usr/local/bin/python

class TextReader:

"""Print and number lines in a text file."""

def __init__(self, file):

self.file = file

self.fh = open(file)

self.lineno = 0

def readline(self):

self.lineno = self.lineno + 1

line = self.fh.readline()

if not line:

return None

if line.endswith("\n"):

line = line[:-1]

return "%d: %s" % (self.lineno, line)

def __getstate__(self):

odict = self.__dict__.copy() # copy the dict since we change it

del odict['fh'] # remove filehandle entry

return odict

def __setstate__(self, dict):

fh = open(dict['file']) # reopen file

count = dict['lineno'] # read from file...

while count: # until line count is restored

fh.readline()

count = count - 1

self.__dict__.update(dict) # update attributes

self.fh = fh # save the file object

複制

示例用法可(kě)能(néng)如(rú)下(xià)所示:

>>> import TextReader

>>> obj = TextReader.TextReader("TextReader.py")

>>> obj.readline()

'1: #!/usr/local/bin/python'

>>> obj.readline()

'2: '

>>> obj.readline()

'3: class TextReader:'

>>> import pickle

>>> pickle.dump(obj, open('save.p', 'wb'))

複制

如(rú)果你想看(kàn)到它pickle在Python進程中工(gōng)作(zuò),在繼續之前啓動另一(yī)個Python會話。接下(xià)來可(kě)能(néng)發生在同一(yī)流程或新流程之後。

>>> import pickle

>>> reader = pickle.load(open('save.p', 'rb'))

>>> reader.readline()

'4: """Print and number lines in a text file."""'

網站建設開(kāi)發|APP設計開(kāi)發|小程序建設開(kāi)發