1. 程式人生 > >Python cPickle模組用法

Python cPickle模組用法

轉自http://blog.csdn.net/bh20077/article/details/6070278

永續性就是指保持物件,甚至在多次執行同一程式之間也保持物件。通過本文,您會對 Python物件的各種永續性機制(從關係資料庫到 Python 的 pickle以及其它機制)有一個總體認識。另外,還會讓您更深一步地瞭解Python 的物件序列化能力。

什麼是永續性?
永續性的基本思想很簡單。假定有一個 Python 程式,它可能是一個管理日常待辦事項的程式,您希望在多次執行這個程式之間可以儲存應用程式物件(待辦事項)。換句話說,您希望將物件儲存在磁碟上,便於以後檢索。這就是永續性。要達到這個目的,有幾種方法,每一種方法都有其優缺點。
例如,可以將物件資料儲存在某種格式的文字檔案中,譬如 CSV 檔案。或者可以用關係資料庫,譬如 Gadfly、MySQL、PostgreSQL 或者 DB2。這些檔案格式和資料庫都非常優秀,對於所有這些儲存機制,Python 都有健壯的介面。
這些儲存機制都有一個共同點:儲存的資料是獨立於對這些資料進行操作的物件和程式。這樣做的好處是,資料可以作為共享的資源,供其它應用程式使用。缺點是,用這種方式,可以允許其它程式訪問物件的資料,這違背了面向物件的封裝性原則 — 即物件的資料只能通過這個物件自身的公共(public)介面來訪問。
另外,對於某些應用程式,關係資料庫方法可能不是很理想。尤其是,關係資料庫不理解物件。相反,關係資料庫會強行使用自己的型別系統和關係資料模型(表),每張表包含一組元組(行),每行包含具有固定數目的靜態型別欄位(列)。如果應用程式的物件模型不能夠方便地轉換到關係模型,那麼在將物件對映到元組以及將元組映射回物件方面,會碰到一定難度。這種困難常被稱為阻礙性不匹配(impedence-mismatch)問題。
物件永續性
如果希望透明地儲存 Python 物件,而不丟失其身份和型別等資訊,則需要某種形式的物件序列化:它是一個將任意複雜的物件轉成物件的文字或二進位制表示的過程。同樣,必須能夠將物件經過序列化後的形式恢復到原有的物件。在 Python 中,這種序列化過程稱為 pickle,可以將物件 pickle 成字串、磁碟上的檔案或者任何類似於檔案的物件,也可以將這些字串、檔案或任何類似於檔案的物件 unpickle 成原來的物件。我們將在本文後面詳細討論 pickle。
假定您喜歡將任何事物都儲存成物件,而且希望避免將物件轉換成某種基於非物件儲存的開銷;那麼 pickle 檔案可以提供這些好處,但有時可能需要比這種簡單的 pickle 檔案更健壯以及更具有可伸縮性的事物。例如,只用 pickle 不能解決命名和查詢 pickle 檔案這樣的問題,另外,它也不能支援併發地訪問永續性物件。如果需要這些方面的功能,則要求助類似於 ZODB(針對 Python 的 Z 物件資料庫)這類資料庫。ZODB 是一個健壯的、多使用者的和麵向物件的資料庫系統,它能夠儲存和管理任意複雜的 Python 物件,並支援事務操作和併發控制。(請參閱 參考資料,以下載 ZODB。)令人足夠感興趣的是,甚至 ZODB 也依靠 Python 的本機序列化能力,而且要有效地使用 ZODB,必須充分了解 pickle。
另一種令人感興趣的解決永續性問題的方法是 Prevayler,它最初是用 Java 實現的(有關 Prevaylor 方面的developerWorks 文章,請參閱 參考資料)。最近,一群 Python 程式設計師將 Prevayler 移植到了 Python 上,另起名為 PyPerSyst,由 SourceForge 託管(有關至 PyPerSyst 專案的連結,請參閱 參考資料)。Prevayler/PyPerSyst 概念也是建立在 Java 和 Python 語言的本機序列化能力之上。PyPerSyst 將整個物件系統儲存在記憶體中,並通過不時地將系統快照 pickle 到磁碟以及維護一個命令日誌(通過此日誌可以重新應用最新的快照)來提供災難恢復。所以,儘管使用 PyPerSyst 的應用程式受到可用記憶體的限制,但好處是本機物件系統可以完全裝入到記憶體中,因而速度極快,而且實現起來要比如 ZODB 這樣的資料庫簡單,ZODB 允許物件的數目比同時在能記憶體中所保持的物件要多。
既然我們已經簡要討論了儲存持久物件的各種方法,那麼現在該詳細探討 pickle 過程了。雖然我們主要感興趣的是探索以各種方式來儲存 Python 物件,而不必將其轉換成某種其它格式,但我們仍然還有一些需要關注的地方,譬如:如何有效地 pickle 和 unpickle 簡單物件以及複雜物件,包括定製類的例項;如何維護物件的引用,包括迴圈引用和遞迴引用;以及如何處理類定義發生的變化,從而使用以前經過 pickle 的例項時不會發生問題。我們將在隨後關於 Python 的 pickle 能力探討中涉及所有這些問題。
一些經過 pickle 的 Python
pickle 模組及其同類模組 cPickle 向 Python 提供了 pickle 支援。後者是用 C 編碼的,它具有更好的效能,對於大多數應用程式,推薦使用該模組。我們將繼續討論 pickle ,但本文的示例實際是利用了 cPickle 。由於其中大多數示例要用 Python shell 來顯示,所以先展示一下如何匯入 cPickle ,並可以作為 pickle 來引用它:
>>> import cPickle as pickle
現在已經匯入了該模組,接下來讓我們看一下 pickle 介面。 pickle 模組提供了以下函式對: dumps(object) 返回一個字串,它包含一個 pickle 格式的物件; loads(string) 返回包含在 pickle 字串中的物件; dump(object, file) 將物件寫到檔案,這個檔案可以是實際的物理檔案,但也可以是任何類似於檔案的物件,這個物件具有 write() 方法,可以接受單個的字串引數; load(file) 返回包含在 pickle 檔案中的物件。
預設情況下, dumps() 和 dump() 使用可列印的 ASCII 表示來建立 pickle。兩者都有一個 final 引數(可選),如果為 True ,則該引數指定用更快以及更小的二進位制表示來建立 pickle。 loads() 和 load() 函式自動檢測 pickle 是二進位制格式還是文字格式。
清單 1 顯示了一個互動式會話,這裡使用了剛才所描述的 dumps() 和 loads() 函式:
清單 1. dumps() 和 loads() 的演示
  1. >>> import cPickle as pickle  
  2. >>> t1 = ('this is a string'42, [123], None)  
  3. >>> t1  
  4. ('this is a string'42, [123], None)  
  5. >>> p1 = pickle.dumps(t1)  
  6. >>> p1  
  7. "(S'this is a string'/nI42/n(lp1/nI1/naI2/naI3/naNtp2/n."
  8. >>> print p1  
  9. (S'this is a string'
  10. I42  
  11. (lp1  
  12. I1  
  13. aI2  
  14. aI3  
  15. aNtp2  
  16. .  
  17. >>> t2 = pickle.loads(p1)  
  18. >>> t2  
  19. ('this is a string'42, [123], None)  
  20. >>> p2 = pickle.dumps(t1, True)  
  21. >>> p2  
  22. '(U/x10this is a stringK*]q/x01(K/x01K/x02K/x03eNtq/x02.'
  23. >>> t3 = pickle.loads(p2)  
  24. >>> t3  
  25. ('this is a string'42, [123], None)  
 
注:該文字 pickle 格式很簡單,這裡就不解釋了。事實上,在 pickle 模組中記錄了所有使用的約定。我們還應該指出,在我們的示例中使用的都是簡單物件,因此使用二進位制 pickle 格式不會在節省空間上顯示出太大的效率。然而,在實際使用複雜物件的系統中,您會看到,使用二進位制格式可以在大小和速度方面帶來顯著的改進。
接下來,我們看一些示例,這些示例用到了 dump() 和 load() ,它們使用檔案和類似檔案的物件。這些函式的操作非常類似於我們剛才所看到的 dumps() 和 loads() ,區別在於它們還有另一種能力 — dump() 函式能一個接著一個地將幾個物件轉儲到同一個檔案。隨後呼叫 load() 來以同樣的順序檢索這些物件。清單 2 顯示了這種能力的實際應用:
清單 2. dump() 和 load() 示例
  1. >>> a1 = 'apple'
  2. >>> b1 = {1'One'2'Two'3'Three'}  
  3. >>> c1 = ['fee''fie''foe''fum']  
  4. >>> f1 = file('temp.pkl''wb')  
  5. >>> pickle.dump(a1, f1, True)  
  6. >>> pickle.dump(b1, f1, True)  
  7. >>> pickle.dump(c1, f1, True)  
  8. >>> f1.close()  
  9. >>> f2 = file('temp.pkl''rb')  
  10. >>> a2 = pickle.load(f2)  
  11. >>> a2  
  12. 'apple'
  13. >>> b2 = pickle.load(f2)  
  14. >>> b2  
  15. {1'One'2'Two'3'Three'}  
  16. >>> c2 = pickle.load(f2)  
  17. >>> c2  
  18. ['fee''fie''foe''fum']  
  19. >>> f2.close()  
 
Pickle 的威力
到目前為止,我們講述了關於 pickle 方面的基本知識。在這一節,將討論一些高階問題,當您開始 pickle 複雜物件時,會遇到這些問題,其中包括定製類的例項。幸運的是,Python 可以很容易地處理這種情形。
可移植性
從空間和時間上說,Pickle 是可移植的。換句話說,pickle 檔案格式獨立於機器的體系結構,這意味著,例如,可以在 Linux 下建立一個 pickle,然後將它傳送到在 Windows 或 Mac OS 下執行的 Python 程式。並且,當升級到更新版本的 Python 時,不必擔心可能要廢棄已有的 pickle。Python 開發人員已經保證 pickle 格式將可以向後相容 Python 各個版本。事實上,在 pickle 模組中提供了有關目前以及所支援的格式方面的詳細資訊.
清單 3. 檢索所支援的格式
  1. >>> pickle.format_version  
  2. '1.3'
  3. >>> pickle.compatible_formats  
  4. ['1.0''1.1''1.2']  
 
多個引用,同一物件
在 Python 中,變數是物件的引用。同時,也可以用多個變數引用同一個物件。經證明,Python 在用經過 pickle 的物件維護這種行為方面絲毫沒有困難,如清單 4 所示:
清單 4. 物件引用的維護
  1. >>> a = [123]  
  2. >>> b = a  
  3. >>> a  
  4. [123]  
  5. >>> b  
  6. [123]  
  7. >>> a.append(4)  
  8. >>> a  
  9. [1234]  
  10. >>> b  
  11. [1234]  
  12. >>> c = pickle.dumps((a, b))  
  13. >>> d, e = pickle.loads(c)  
  14. >>> d  
  15. [1234]  
  16. >>> e  
  17. [1234]  
  18. >>> d.append(5)  
  19. >>> d  
  20. [12345]  
  21. >>> e  
  22. [12345]  
 
迴圈引用和遞迴引用
可以將剛才演示過的物件引用支援擴充套件到 迴圈引用(兩個物件各自包含對對方的引用)和 遞迴引用(一個物件包含對其自身的引用)。下面兩個清單著重顯示這種能力。我們先看一下遞迴引用:
>清單 5. 遞迴引用
  1. >>> l = [123]  
  2. >>> l.append(l)  
  3. >>> l  
  4. [123, [...]]  
  5. >>> l[3]  
  6. [123, [...]]  
  7. >>> l[3][3]  
  8. [123, [...]]  
  9. >>> p = pickle.dumps(l)  
  10. >>> l2 = pickle.loads(p)  
  11. >>> l2  
  12. [123, [...]]  
  13. >>> l2[3]  
  14. [123, [...]]  
  15. >>> l2[3][3]  
  16. [123, [...]]  
 
現在,看一個迴圈引用的示例:
清單 6. 迴圈引用
  1. >>> a = [12]  
  2. >>> b = [34]  
  3. >>> a.append(b)  
  4. >>> a  
  5. [12, [34]]  
  6. >>> b.append(a)  
  7. >>> a  
  8. [12, [34, [...]]]  
  9. >>> b  
  10. [34, [12, [...]]]  
  11. >>> a[2]  
  12. [34, [12, [...]]]  
  13. >>> b[2]  
  14. [12, [34, [...]]]  
  15. >>> a[2is b  
  16. 1
  17. >>> b[2is a  
  18. 1
  19. >>> f = file('temp.pkl''w')  
  20. >>> pickle.dump((a, b), f)  
  21. >>> f.close()  
  22. >>> f = file('temp.pkl''r')  
  23. >>> c, d = pickle.load(f)  
  24. >>> f.close()  
  25. >>> c  
  26. [12, [34, [...]]]  
  27. >>> d  
  28. [34, [12, [...]]]  
  29. >>> c[2]  
  30. [34, [12, [...]]]  
  31. >>> d[2]  
  32. [12, [34, [...]]]  
  33. >>> c[2is d  
  34. 1
  35. >>> d[2is c  
  36. 1
 
注意,如果分別 pickle 每個物件,而不是在一個元組中一起 pickle 所有物件,會得到略微不同(但很重要)的結果,如清單 7 所示:
清單 7. 分別 pickle vs. 在一個元組中一起 pickle
  1. >>> f = file('temp.pkl''w')  
  2. >>> pickle.dump(a, f)  
  3. >>> pickle.dump(b, f)  
  4. >>> f.close()  
  5. >>> f = file('temp.pkl''r')  
  6. >>> c = pickle.load(f)  
  7. >>> d = pickle.load(f)  
  8. >>> f.close()  
  9. >>> c  
  10. [12, [34, [...]]]  
  11. >>> d  
  12. [34, [12, [...]]]  
  13. >>> c[2]  
  14. [34, [12, [...]]]  
  15. >>> d[2]  
  16. [12, [34, [...]]]  
  17. >>> c[2is d  
  18. 0
  19. >>> d[2is c  
  20. 0
 
相等,但並不總是相同
正如在上一個示例所暗示的,只有在這些物件引用記憶體中同一個物件時,它們才是相同的。在 pickle 情形中,每個物件被恢復到一個與原來物件相等的物件,但不是同一個物件。換句話說,每個 pickle 都是原來物件的一個副本:
清單 8. 作為原來物件副本的被恢復的物件
  1. >>> j = [123]  
  2. >>> k = j  
  3. >>> k is j  
  4. 1
  5. >>> x = pickle.dumps(k)  
  6. >>> y = pickle.loads(x)  
  7. >>> y  
  8. [123]  
  9. >>> y == k  
  10. 1
  11. >>> y is k  
  12. 0
  13. >>> y is j  
  14. 0
  15. >>> k is j  
  16. 1
 
同時,我們看到 Python 能夠維護物件之間的引用,這些物件是作為一個單元進行 pickle 的。然而,我們還看到分別呼叫 dump() 會使 Python 無法維護對在該單元外部進行 pickle 的物件的引用。相反,Python 複製了被引用物件,並將副本和被 pickle 的物件儲存在一起。對於 pickle 和恢復單個物件層次結構的應用程式,這是沒有問題的。但要意識到還有其它情形。
值得指出的是,有一個選項確實允許分別 pickle 物件,並維護相互之間的引用,只要這些物件都是 pickle 到同一檔案即可。 pickle 和 cPickle 模組提供了一個 Pickler (與此相對應是 Unpickler ),它能夠跟蹤已經被 pickle 的物件。通過使用這個 Pickler ,將會通過引用而不是通過值來 pickle 共享和迴圈引用:
清單 9. 維護分別 pickle 的物件間的引用
  1. >>> f = file('temp.pkl''w')  
  2. >>> pickler = pickle.Pickler(f)  
  3. >>> pickler.dump(a)  
  4. <cPickle.Pickler object at 0x89b0bb8>  
  5. >>> pickler.dump(b)  
  6. <cPickle.Pickler object at 0x89b0bb8>  
  7. >>> f.close()  
  8. >>> f = file('temp.pkl''r')  
  9. >>> unpickler = pickle.Unpickler(f)  
  10. >>> c = unpickler.load()  
  11. >>> d = unpickler.load()  
  12. >>> c[2]  
  13. [34, [12, [...]]]  
  14. >>> d[2]  
  15. [12, [34, [...]]]  
  16. >>> c[2is d  
  17. 1
  18. >>> d[2is c  
  19. 1
 
不可 pickle 的物件
一些物件型別是不可 pickle 的。例如,Python 不能 pickle 檔案物件(或者任何帶有對檔案物件引用的物件),因為 Python 在 unpickle 時不能保證它可以重建該檔案的狀態(另一個示例比較難懂,在這類文章中不值得提出來)。試圖 pickle 檔案物件會導致以下錯誤:
清單 10. 試圖 pickle 檔案物件的結果
  1. >>> f = file('temp.pkl''w')  
  2. >>> p = pickle.dumps(f)  
  3. Traceback (most recent call last):  
  4.   File "<input>", line 1in ?  
  5.   File "/usr/lib/python2.2/copy_reg.py", line 57in _reduce  
  6.     raise TypeError, "can't pickle %s objects" % base.__name__  
  7. TypeError: can't pickle file objects  
 
類例項
與 pickle 簡單物件型別相比,pickle 類例項要多加留意。這主要由於 Python 會 pickle 例項資料(通常是 _dict_ 屬性)和類的名稱,而不會 pickle 類的程式碼。當 Python unpickle 類的例項時,它會試圖使用在 pickle 該例項時的確切的類名稱和模組名稱(包括任何包的路徑字首)匯入包含該類定義的模組。另外要注意,類定義必須出現在模組的最頂層,這意味著它們不能是巢狀的類(在其它類或函式中定義的類)。
當 unpickle 類的例項時,通常不會再呼叫它們的 _init_() 方法。相反,Python 建立一個通用類例項,並應用已進行過 pickle 的例項屬性,同時設定該例項的 _class_ 屬性,使其指向原來的類。
對 Python 2.2 中引入的新型類進行 unpickle 的機制與原來的略有不同。雖然處理的結果實際上與對舊型類處理的結果相同,但 Python 使用 copy_reg 模組的 _reconstructor() 函式來恢復新型類的例項。
如果希望對新型或舊型類的例項修改預設的 pickle 行為,則可以定義特殊的類的方法 _getstate_() 和 _setstate_() ,在儲存和恢復類例項的狀態資訊期間,Python 會呼叫這些方法。在以下幾節中,我們會看到一些示例利用了這些特殊的方法。
現在,我們看一個簡單的類例項。首先,建立一個 persist.py 的 Python 模組,它包含以下新型類的定義:
清單 11. 新型類的定義
  1. class Foo(object):  
  2.     def __init__(self, value):  
  3.         self.value = value  
 
現在可以 pickle Foo 例項,並看一下它的表示:
清單 12. pickle Foo 例項
  1. >>> import cPickle as pickle  
  2. >>> from Orbtech.examples.persist import Foo  
  3. >>> foo = Foo('What is a Foo?')  
  4. >>> p = pickle.dumps(foo)  
  5. >>> print p  
  6. ccopy_reg  
  7. _reconstructor  
  8. p1  
  9. (cOrbtech.examples.persist  
  10. Foo  
  11. p2  
  12. c__builtin__  
  13. object  
  14. p3  
  15. NtRp4  
  16. (dp5  
  17. S'value'
  18. p6  
  19. S'What is a Foo?'
  20. sb.  
 
可以看到這個類的名稱 Foo 和全限定的模組名稱 Orbtech.examples.persist 都儲存在 pickle 中。如果將這個例項 pickle 成一個檔案,稍後再 unpickle 它或在另一臺機器上 unpickle,則 Python 會試圖匯入 Orbtech.examples.persist 模組,如果不能匯入,則會丟擲異常。如果重新命名該類和該模組或者將該模組移到另一個目錄,則也會發生類似的錯誤。
這裡有一個 Python 發出錯誤訊息的示例,當我們重新命名 Foo 類,然後試圖裝入先前進行過 pickle 的 Foo 例項時會發生該錯誤:
清單 13. 試圖裝入一個被重新命名的 Foo 類的經過 pickle 的例項
  1. >>> import cPickle as pickle  
  2. >>> f = file('temp.pkl''r')  
  3. >>> foo = pickle.load(f)  
  4. Traceback (most recent call last):  
  5.   File "<input>", line 1in ?  
  6. AttributeError: 'module' object has no attribute 'Foo'
 
在重新命名 persist.py 模組之後,也會發生類似的錯誤:
清單 14. 試圖裝入一個被重新命名的 persist.py 模組的經過 pickle 的例項
  1. >>> import cPickle as pickle  
  2. >>> f = file('temp.pkl''r')  
  3. >>> foo = pickle.load(f)  
  4. Traceback (most recent call last):  
  5.   File "<input>", line 1in ?  
  6. ImportError: No module named persist  
 
我們會在下面 模式改進這一節提供一些技術來管理這類更改,而不會破壞現有的 pickle。
特殊的狀態方法
前面提到對一些物件型別(譬如,檔案物件)不能進行 pickle。處理這種不能 pickle 的物件的例項屬性時可以使用特殊的方法( _getstate_() 和 _setstate_() )來修改類例項的狀態。這裡有一個 Foo 類的示例,我們已經對它進行了修改以處理檔案物件屬性:
清單 15. 處理不能 pickle 的例項屬性
  1. class Foo(object):  
  2.     def __init__(self, value, filename):  
  3.         self.value = value  
  4.         self.logfile = file(filename, 'w')  
  5.     def __getstate__(self):  
  6.         """Return state values to be pickled."""
  7.         f = self.logfile  
  8.         return (self.value, f.name, f.tell())  
  9.     def __setstate__(self, state):  
  10.         """Restore state from the unpickled state values."""
  11.         self.value, name, position = state  
  12.         f = file(name, 'w')  
  13.         f.seek(position)  
  14.         self.logfile = f  
 
pickle Foo 的例項時,Python 將只 pickle 當它呼叫該例項的 _getstate_() 方法時返回給它的值。類似的,在 unpickle 時,Python 將提供經過 unpickle 的值作為引數傳遞給例項的 _setstate_() 方法。在 _setstate_() 方法內,可以根據經過 pickle 的名稱和位置資訊來重建檔案物件,並將該檔案物件分配給這個例項的 logfile 屬性。
模式改進
隨著時間的推移,您會發現自己必須要更改類的定義。如果已經對某個類例項進行了 pickle,而現在又需要更改這個類,則您可能要檢索和更新那些例項,以便它們能在新的類定義下繼續正常工作。而我們已經看到在對類或模組進行某些更改時,會出現一些錯誤。幸運的是,pickle 和 unpickle 過程提供了一些 hook,我們可以用它們來支援這種模式改進的需要。
在這一節,我們將探討一些方法來預測常見問題以及如何解決這些問題。由於不能 pickle 類例項程式碼,因此可以新增、更改和除去方法,而不會影響現有的經過 pickle 的例項。出於同樣的原因,可以不必擔心類的屬性。您必須確保包含類定義的程式碼模組在 unpickle 環境中可用。同時還必須為這些可能導致 unpickle 問題的更改做好規劃,這些更改包括:更改類名、新增或除去例項的屬性以及改變類定義模組的名稱或位置。
類名的更改
要更改類名,而不破壞先前經過 pickle 的例項,請遵循以下步驟。首先,確保原來的類的定義沒有被更改,以便在 unpickle 現有例項時可以找到它。不要更改原來的名稱,而是在與原來類定義所在的同一個模組中,建立該類定義的一個副本,同時給它一個新的類名。然後使用實際的新類名來替代 NewClassName ,將以下方法新增到原來類的定義中:
清單 16. 更改類名:新增到原來類定義的方法
  1. def __setstate__(self, state):  
  2.     self.__dict__.update(state)  
  3.     self.__class__ = NewClassName  
 
當 unpickle 現有例項時,Python 將查詢原來類的定義,並呼叫例項的 _setstate_() 方法,同時將給新的類定義重新分配該例項的 _class_ 屬性。一旦確定所有現有的例項都已經 unpickle、更新和重新 pickle 後,可以從原始碼模組中除去舊的類定義。
屬性的新增和刪除
這些特殊的狀態方法 _getstate_() 和 _setstate_() 再一次使我們能控制每個例項的狀態,並使我們有機會處理例項屬性中的更改。讓我們看一個簡單的類的定義,我們將向其新增和除去一些屬性。這是是最初的定義:
清單 17. 最初的類定義
  1. class Person(object):  
  2.     def __init__(self, firstname, lastname):  
  3.         self.firstname = firstname  
  4.         self.lastname = lastname  
 
假定已經建立並 pickle 了 Person 的例項,現在我們決定真的只想儲存一個名稱屬性,而不是分別儲存姓和名。這裡有一種方式可以更改類的定義,它將先前經過 pickle 的例項遷移到新的定義:
清單 18. 新的類定義
  1. class Person(object):  
  2.     def __init__(self, fullname):  
  3.         self.fullname = fullname  
  4.     def __setstate__(self, state):  
  5.         if'fullname'notin state:  
  6.             first = ''
  7.             last = ''
  8.             if'firstname'in state:  
  9.                 first = state['firstname']  
  10.                 del state['firstname']  
  11.             if'lastname'in state:  
  12.                 last = state['lastname']  
  13.                 del state['lastname']  
  14.             self.fullname = " ".join([first, last]).strip()  
  15.         self.__dict__.update(state)  
 
在這個示例,我們添加了一個新的屬性 fullname ,併除去了兩個現有的屬性 firstname 和 lastname 。當對先前進行過 pickle 的例項執行 unpickle 時,其先前進行過 pickle 的狀態會作為字典傳遞給 _setstate_() ,它將包括 firstname 和 lastname 屬性的值。接下來,將這兩個值組合起來,並將它們分配給新屬性 fullname 。在這個過程中,我們刪除了狀態字典中舊的屬性。更新和重新 pickle 先前進行過 pickle 的所有例項之後,現在可以從類定義中除去 _setstate_() 方法。
模組的修改
在概念上,模組的名稱或位置的改變類似於類名稱的改變,但處理方式卻完全不同。那是因為模組的資訊儲存在 pickle 中,而不是通過標準的 pickle 介面就可以修改的屬性。事實上,改變模組資訊的唯一辦法是對實際的 pickle 檔案本身執行查詢和替換操作。至於如何確切地去做,這取決於具體的作業系統和可使用的工具。很顯然,在這種情況下,您會想備份您的檔案,以免發生錯誤。但這種改動應該非常簡單,並且對二進位制 pickle 格式進行更改與對文字 pickle 格式進行更改應該一樣有效。