做自(zì)由與創造的先行者

數據持久性 | Data Persistence pickle

Python開(kāi)發手冊

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

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

警告

該pickle模塊對(duì)于錯誤或惡意構建的數據不安全。切勿取消從(cóng)不可信或未經認證的來(lái)源收到(dào)的數據。

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

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

兩個模塊産生的數據流保證可以互換。

Python有一個更原始的序列化模塊marshal,但(dàn)通常pickle應該是序列化Python對(duì)象的首選方式。marshal主要是爲了(le)支持Python的.pyc文(wén)件。

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

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

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

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

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

2.數據流格式

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

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

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

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

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

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

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

如果一個協議(yì)沒有指定,協議(yì)0被使用(yòng)。如果協議(yì)被指定爲負值,或HIGHEST_PROTOCOL将使用(yòng)可用(yòng)的最高(gāo)協議(yì)版本。

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

可以通過指定協議(yì)版本> = 1 來(lái)選擇稍微更高(gāo)效的二進制格式。

3.用(yòng)法

要序列化對(duì)象層次結構,首先創建一個pickler,然後調用(yòng)pickler的dump()方法。爲了(le)反序列化數據流,首先創建一個unpickler,然後調用(yòng)unpickler的load()方法。該pickle模塊提供以下(xià)常數:

pickle.HIGHEST_PROTOCOL

可用(yòng)的最高(gāo)協議(yì)版本。該值可以作(zuò)爲協議(yì)值傳遞。

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

注意

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

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

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

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

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

如果協議(yì)參數被省略,則使用(yòng)協議(yì)0。如果協議(yì)被指定爲負值,或者HIGHEST_PROTOCOL将使用(yòng)最高(gāo)協議(yì)版本。

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

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

pickle.load(file)

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

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

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

pickle.dumps(obj[, protocol])

将對(duì)象的pickled表示形式返回爲字符串,而不是将其寫入文(wén)件。

如果協議(yì)參數被省略,則使用(yòng)協議(yì)0。如果協議(yì)被指定爲負值,或者HIGHEST_PROTOCOL将使用(yòng)最高(gāo)協議(yì)版本。

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

pickle.loads(string)

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

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

exception pickle.PickleError

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

exception pickle.PicklingError

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

exception pickle.UnpicklingError

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

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

class pickle.Pickler(file[, protocol])

這(zhè)需要一個文(wén)件類對(duì)象,它将寫入一個pickle數據流。

如果協議(yì)參數被省略,則使用(yòng)協議(yì)0。如果協議(yì)被指定爲負值,或者HIGHEST_PROTOCOL将使用(yòng)最高(gāo)協議(yì)版本。

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

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

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

dump(obj)

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

clear_memo()

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

注意

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

mypickler.memo.clear()

複制

不需要支持較舊版本的Python的代碼應該簡單地使用(yòng)clear_memo()。

可以對(duì)dump()同一個Pickler實例的方法進行多次調用(yòng)。然後這(zhè)些(xiē)必須匹配到(dào)load()相應Unpickler實例的方法的相同數量的調用(yòng)。如果同一個對(duì)象被多次dump()調用(yòng)腌制,那麽這(zhè)個load()将全部産生對(duì)同一個對(duì)象的引用(yòng)。[3]

Unpickler 對(duì)象被定義爲:

class pickle.Unpickler(file)

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

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

Unpickler 對(duì)象有一個(或兩個)公共方法:

load()

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

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

noload()

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

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

4.什(shén)麽可以 pickled和(hé)unpickled?

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

None, True, and False

整數,長整數,浮點數,複數

正常和(hé)Unicode字符串

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

函數定義在模塊的頂層

在模塊頂層定義的内置函數

在模塊頂層定義的類

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

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

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

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

class Foo:

attr = 'a class attr'

picklestring = pickle.dumps(Foo)

複制

這(zhè)些(xiē)限制是爲什(shén)麽必須在模塊的頂層定義可調用(yòng)的函數和(hé)類。

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

5.pickle協議(yì)

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

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

object.__getinitargs__()

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

object.__getnewargs__()

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

obj = C.__new__(C, *args)

複制

其中ARGS是調用(yòng)的結果而__getnewargs__()原來(lái)的對(duì)象上(shàng); 如果不存在__getnewargs__(),則假定一個空(kōng)元組。

object.__getstate__()

課程可以進一步影響他(tā)們的實例如何腌制; 如果類定義了(le)該方法__getstate__(),則會(huì)調用(yòng)該方法,并将返回狀态作(zuò)爲實例的内容進行挑選,而不是實例字典的内容。如果沒有__getstate__()方法,則實例__dict__被腌制。

object.__setstate__(state)

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

Note

對(duì)于新樣式類,如果__getstate__()返回一個假值,則該__setstate__()方法不會(huì)被調用(yòng)。

注意

在在unpickle時(shí),一些(xiē)方法,如__getattr__(),__getattribute__()或__setattr__()可在該實例調用(yòng)。如果這(zhè)些(xiē)方法依賴于一些(xiē)内部不變爲真,則類型應該實現(xiàn)任一__getinitargs__()或__getnewargs__()建立這(zhè)樣的不變的; 否則,既__new__()不會(huì)也(yě)__init__()不會(huì)被調用(yòng)。

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

object.__reduce__()

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

如果返回一個字符串,它就會(huì)命名一個全局變量,其内容被正常腌制。返回的字符串__reduce__()應該是相對(duì)于其模塊的對(duì)象的本地名稱; pickle模塊搜索模塊名稱空(kōng)間以确定對(duì)象的模塊。

當一個元組返回時(shí),它的長度必須在2到(dào)5個元素之間。可選元素可以省略,None也(yě)可以作(zuò)爲它們的值提供。這(zhè)個元組的内容按照正常方式進行腌制,并且在取出時(shí)用(yòng)于重建對(duì)象。每個元素的語義是:

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

可調用(yòng)對(duì)象的參數元組。

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

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

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

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

object.__reduce_ex__(protocol)

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

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

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

如上(shàng)所述,已注冊的構造函數被視(shì)爲“安全構造函數”,用(yòng)于拆除目的。

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

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

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

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

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

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

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

6.子類化Unpicklers

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

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

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

這(zhè)個故事(shì)的寓意是你(nǐ)應該非常小(xiǎo)心你(nǐ)的應用(yòng)程序取消選擇的字符串的來(lái)源。

7.例子

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

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à)示例讀取所産生的腌制數據。當讀取含有腌菜的文(wén)件時(shí),應該以二進制模式打開(kāi)文(wén)件,因爲您無法确定是否使用(yòng)了(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à)面是一個更大(dà)的例子,展示了(le)如何修改一個類的酸洗行爲。本TextReader類打開(kāi)一個文(wén)本文(wén)件,并返回每一次它的行号和(hé)行内容,readline()方法被調用(yòng)。如果一個TextReader實例被腌制,除文(wén)件對(duì)象成員之外(wài)的所有屬性都将被保存。當實例取消選中時(shí),将重新打開(kāi)該文(wén)件,并從(cóng)最後一個位置繼續讀取。該__setstate__()和(hé)__getstate__()方法來(lái)實現(xiàn)此行爲。

#!/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

複制

示例用(yòng)法可能(néng)如下(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'))

複制

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

>>> import pickle

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

>>> reader.readline()

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

網站(zhàn)建設開(kāi)發|APP設計(jì)開(kāi)發|小(xiǎo)程序建設開(kāi)發
下(xià)一篇:數據持久性 | Data Persistence shelve
上(shàng)一篇:數據持久性 | Data Persistence marshal