python的pickle模塊

python的pickle模塊實現了基本的數據序列和反序列化。通過pickle模塊的序列化操作我們能夠將程序中運行的對象信息保存到文件中去,永久存儲;通過pickle模塊的反序列化操作,我們能夠從文件中創建上一次程序保存的對象

1、什麼東西能用pickle模塊存儲?

所有Python支持的 原生類型 : 布爾, 整數, 浮點數, 複數, 字符串, bytes(字節串)對象, 字節數組, 以及 None.

由任何原生類型組成的列表,元組,字典和集合。

由任何原生類型組成的列表,元組,字典和集合組成的列表,元組,字典和集合(可以一直嵌套下去,直至Python支持的最大遞歸層數).

函數,類,和類的實例(帶警告)。

【摘自《深入Python》】

2、示例(保存數據到PICKLE文件):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#encoding:UTF-8 
import pickle 
   
#序列化 
def dump_pickle(): 
    user={} 
    user['id']=1 
    user['name']='tanweijie' 
    user['email']='[email protected]' 
    user['sex']='boy' 
   
    #with保證自動關閉文件 
    #設置文件模式爲'wb'來以二進制寫模式打開文件 
    with open('C:/Users/Mr_Tank_/Desktop/user.pickle','wb') as f: 
        #dump()函數接受一個可序列化的Python數據結構 
        pickle.dump(user,f) 
        print('success'
   
#反序列化 
def load_pickle(): 
    with open('C:/Users/Mr_Tank_/Desktop/user.pickle','rb') as f: 
        user=pickle.load(f) 
    #user變量是一個字典     
    print(user)  

3、結果:

?
1
2
3
4
>>> dump_pickle() 
success 
>>> load_pickle() 
{'id': 1, 'name': 'tanweijie', 'sex': 'boy', 'email': '[email protected]'

使用對象pickle

第一次聽到pickle,可能覺得有點複雜,但好消息是,Python隱藏了所有從對象到字符串轉換的複雜性。事實上,pickle模塊的接口簡單易用,簡直令人難以置信。例如,要pickle對象到一個序列化字符串,我們可以生成一個pickler,並調用其方法,或使用模塊中的便捷函數來達到相同的效果:

dump把pickler序列化

生成一個新的pickler,用來pickle到一個打開的輸出文件對象file:

P = pickle.Pickler( file) 
  • 1

寫一個對象到pickler的文件/流:

P.dump( object) 
  • 1

等同於上兩個調用的組合:pickle對象到一個打開的文件:

pickle.dump( object, file) 
  • 1

返回一個字符串作爲已pickle對象的表達:

string = pickle.dumps( object) 
  • 1

load將pickler反序列化

從一個序列化字符串unpickle回原始對象是類似的,可以用對象也可以用便捷函數接口:

生成一個unpickler,用來從一個打開的文件對象file unpickle:

U = pickle.Unpickler( file) 
  • 1

從unpickler的文件/流讀取一個對象:

object = U.load( )
  • 1

等同於上兩個調用的組合:從一個打開的文件unpickle一個對象:

object = pickle.load( file) 
  • 1

從字符串讀取一個對象,而不是從文件:

object = pickle.loads( string) 
  • 1

Pickler和Unpickler是導出類。在上述所有情況下,file是個已打開的文件對象,或者是實現了以下文件對象屬性的任何對象:

Pickler會調用文件的write方法,參數是個字符串。

Unpickler會調用文件的read方法,參數是字節數,以及readline,無參數。

任何提供這些屬性的對象都可以作爲file參數傳入。特別是,file可以是一個提供了讀/寫方法的Python類實例(即預期的類似文件的接口)。這讓您可以用類映射pickle流到內存對象,並可任意使用。例

該掛鉤也可以讓您通過網絡傳輸Python對象,只要封裝套接口,使之看上去像發送端pickle調用中的文件,以及像接收端unpickle調用中的文件。事實上,對一些人來說,pickle Python對象並在一個值得信賴的網絡上傳輸,是替代如SOAP和XML-RPC之類網絡傳輸協議的一個簡單方法;只要通信的兩端都有Python(被pickle的對象是用Python專有的格式表達的,而不是用XML文本)。

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 是二進制格式還是文本格式。

dumps() 和 loads() 的演示

>>> import cPickle as pickle  
>>> t1 = ('this is a string', 42, [1, 2, 3], None)  
>>> t1  
('this is a string', 42, [1, 2, 3], None)  
>>> p1 = pickle.dumps(t1)  
>>> p1  
"(S'this is a string'/nI42/n(lp1/nI1/naI2/naI3/naNtp2/n."  
>>> print p1  
(S'this is a string'  
I42  
(lp1  
I1  
aI2  
aI3  
aNtp2  
.  
>>> t2 = pickle.loads(p1)  
>>> t2  
('this is a string', 42, [1, 2, 3], None)  
>>> p2 = pickle.dumps(t1, True)  
>>> p2  
'(U/x10this is a stringK*]q/x01(K/x01K/x02K/x03eNtq/x02.'  
>>> t3 = pickle.loads(p2)  
>>> t3  
('this is a string', 42, [1, 2, 3], None)  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

注:該文本 pickle 格式很簡單,這裏就不解釋了。事實上,在 pickle 模塊中記錄了所有使用的約定。我們還應該指出,在我們的示例中使用的都是簡單對象,因此使用二進制 pickle 格式不會在節省空間上顯示出太大的效率。然而,在實際使用複雜對象的系統中,您會看到,使用二進制格式可以在大小和速度方面帶來顯著的改進。

dump() 和 load() 示例

這些示例用到了 dump() 和 load() ,它們使用文件和類似文件的對象。這些函數的操作非常類似於我們剛纔所看到的 dumps() 和 loads() ,區別在於它們還有另一種能力 — dump() 函數能一個接着一個地將幾個對象轉儲到同一個文件。隨後調用 load() 來以同樣的順序檢索這些對象。清單 2 顯示了這種能力的實際應用:

>>> a1 = 'apple'  
>>> b1 = {1: 'One', 2: 'Two', 3: 'Three'}  
>>> c1 = ['fee', 'fie', 'foe', 'fum']  
>>> f1 = file('temp.pkl', 'wb')  
>>> pickle.dump(a1, f1, True)  
>>> pickle.dump(b1, f1, True)  
>>> pickle.dump(c1, f1, True)  
>>> f1.close()  
>>> f2 = file('temp.pkl', 'rb')  
>>> a2 = pickle.load(f2)  
>>> a2  
'apple'  
>>> b2 = pickle.load(f2)  
>>> b2  
{1: 'One', 2: 'Two', 3: 'Three'}  
>>> c2 = pickle.load(f2)  
>>> c2  
['fee', 'fie', 'foe', 'fum']  
>>> f2.close()  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

檢索所支持的格式

>>> pickle.format_version  
'1.3'  
>>> pickle.compatible_formats  
['1.0', '1.1', '1.2']  
  • 1
  • 2
  • 3
  • 4

對象引用的維護

在 Python 中,變量是對象的引用。同時,也可以用多個變量引用同一個對象。經證明,Python 在用經過 pickle 的對象維護這種行爲方面絲毫沒有困難

>>> a = [1, 2, 3]  
>>> b = a  
>>> a  
[1, 2, 3]  
>>> b  
[1, 2, 3]  
>>> a.append(4)  
>>> a  
[1, 2, 3, 4]  
>>> b  
[1, 2, 3, 4]  
>>> c = pickle.dumps((a, b))  
>>> d, e = pickle.loads(c)  
>>> d  
[1, 2, 3, 4]  
>>> e  
[1, 2, 3, 4]  
>>> d.append(5)  
>>> d  
[1, 2, 3, 4, 5]  
>>> e  
[1, 2, 3, 4, 5]  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

遞歸引用

>>> l = [1, 2, 3]  
>>> l.append(l)  
>>> l  
[1, 2, 3, [...]]  
>>> l[3]  
[1, 2, 3, [...]]  
>>> l[3][3]  
[1, 2, 3, [...]]  
>>> p = pickle.dumps(l)  
>>> l2 = pickle.loads(p)  
>>> l2  
[1, 2, 3, [...]]  
>>> l2[3]  
[1, 2, 3, [...]]  
>>> l2[3][3]  
[1, 2, 3, [...]]  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

循環引用

>>> a = [1, 2]  
>>> b = [3, 4]  
>>> a.append(b)  
>>> a  
[1, 2, [3, 4]]  
>>> b.append(a)  
>>> a  
[1, 2, [3, 4, [...]]]  
>>> b  
[3, 4, [1, 2, [...]]]  
>>> a[2]  
[3, 4, [1, 2, [...]]]  
>>> b[2]  
[1, 2, [3, 4, [...]]]  
>>> a[2] is b  
1  
>>> b[2] is a  
1  
>>> f = file('temp.pkl', 'w')  
>>> pickle.dump((a, b), f)  
>>> f.close()  
>>> f = file('temp.pkl', 'r')  
>>> c, d = pickle.load(f)  
>>> f.close()  
>>> c  
[1, 2, [3, 4, [...]]]  
>>> d  
[3, 4, [1, 2, [...]]]  
>>> c[2]  
[3, 4, [1, 2, [...]]]  
>>> d[2]  
[1, 2, [3, 4, [...]]]  
>>> c[2] is d  
1  
>>> d[2] is c  
1  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

分別 pickle vs. 在一個元組中一起 pickle

注意,如果分別 pickle 每個對象,而不是在一個元組中一起 pickle 所有對象,會得到略微不同(但很重要)的結果,如下所示:

>>> f = file('temp.pkl', 'w')  
>>> pickle.dump(a, f)  
>>> pickle.dump(b, f)  
>>> f.close()  
>>> f = file('temp.pkl', 'r')  
>>> c = pickle.load(f)  
>>> d = pickle.load(f)  
>>> f.close()  
>>> c  
[1, 2, [3, 4, [...]]]  
>>> d  
[3, 4, [1, 2, [...]]]  
>>> c[2]  
[3, 4, [1, 2, [...]]]  
>>> d[2]  
[1, 2, [3, 4, [...]]]  
>>> c[2] is d  
0  
>>> d[2] is c  
0  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

作爲原來對象副本的被恢復的對象

>>> j = [1, 2, 3]  
>>> k = j  
>>> k is j  
1  
>>> x = pickle.dumps(k)  
>>> y = pickle.loads(x)  
>>> y  
[1, 2, 3]  
>>> y == k  
1  
>>> y is k  
0  
>>> y is j  
0  
>>> k is j  
1  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

維護分別 pickle 的對象間的引用

>>> f = file('temp.pkl', 'w')  
>>> pickler = pickle.Pickler(f)  
>>> pickler.dump(a)  
<cPickle.Pickler object at 0x89b0bb8>  
>>> pickler.dump(b)  
<cPickle.Pickler object at 0x89b0bb8>  
>>> f.close()  
>>> f = file('temp.pkl', 'r')  
>>> unpickler = pickle.Unpickler(f)  
>>> c = unpickler.load()  
>>> d = unpickler.load()  
>>> c[2]  
[3, 4, [1, 2, [...]]]  
>>> d[2]  
[1, 2, [3, 4, [...]]]  
>>> c[2] is d  
1  
>>> d[2] is c  
1  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

試圖 pickle 文件對象的結果

一些對象類型是不可 pickle 的。例如,Python 不能 pickle 文件對象(或者任何帶有對文件對象引用的對象),因爲 Python 在 unpickle 時不能保證它可以重建該文件的狀態(另一個示例比較難懂,在這類文章中不值得提出來)。試圖 pickle 文件對象會導致以下錯誤:

>>> f = file('temp.pkl', 'w')  
>>> p = pickle.dumps(f)  
Traceback (most recent call last):  
  File "<input>", line 1, in ?  
  File "/usr/lib/python2.2/copy_reg.py", line 57, in _reduce  
    raise TypeError, "can't pickle %s objects" % base.__name__  
TypeError: can't pickle file objects  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Pickler協議和cPickle

在最近的Python版本中,pickler推出了協議的概念:pickle數據的保存格式。通過pickle調用時傳入一個額外的參數,可指定所需的協議(但unpickle調用不需要:協議是自動從已pickle的數據確定的):

pickle.dump(object, file, protocol)

Pickle數據可以按文本協議或二進制協議產生。默認情況下,存儲協議是文本協議(也稱爲0號協議)。在文本模式下,用來存儲pickle對象的文件可以用文本模式打開,如上述的例子,並且pickle的數據是可打印的ASCII文本,並且是可讀的(這基本上是對堆棧機實現的指示)。

其他協議(1號和2號協議 )以二進制格式存儲pickle數據,並要求文件以二進制模式打開(例如:rb、wb)。1號協議是原始二進制格式;2號協議是Python 2.3增加的,它改善了對新型類pickle的支持。二進制格式效率更高一點,但它無法進行查看。舊的pickle調用有一個選項,即bin參數,現已被歸入使用大於0的協議。pickle模塊還提供了一個HIGHEST_PROTOCOL變量,傳入它可以自動選擇最大的協議值。

注意:如果您使用默認的文本協議,以後請務必以文本模式打開pickle文件。在一些平臺上,因爲Windows的行尾格式不同,以二進制模式打開文本數據可能會導致unpickle錯誤:

>>> f = open('temp', 'w')                  # text mode file on Windows
>>> pickle.dump(('ex', 'parrot'), f)       # use default text protocol
>>> f.close( ) 
>>>
>>> pickle.load(open('temp', 'r'))         # OK in text mode
('ex', 'parrot')
>>> pickle.load(open('temp', 'rb'))        # fails in binary
Traceback (most recent call last):
  File "<pyshell#337>", line 1, in -toplevel-
    pickle.load(open('temp', 'rb'))
 ...lines deleted...
ValueError: insecure string pickle
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

迴避這個潛在問題的方法之一是,總是使用二進制模式的文件,即使是用文本pickle協議。至少對於二進制pickler協議(高於默認0),您必須以二進制模式打開文件,所以這不是一個壞習慣:

>>> f = open('temp', 'wb')                 # create in binary mode
>>> pickle.dump(('ex', 'parrot'), f)       # use text protocol
>>> f.close( )
>>>
>>> pickle.load(open('temp', 'rb'))
('ex', 'parrot')
>>> pickle.load(open('temp', 'r'))
('ex', 'parrot')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

請參考Python庫手冊,以瞭解更多pickler的信息。另外,請查閱marshal,它也是一個序列化對象的模塊,但只能處理簡單對象類型。pickle比marshal更通用,並通常是首選。

而當你翻看(或點擊)Python手冊時,請一定也要看看cPickle模塊的條目,它是pickle的C語言實現,性能上更快。您可以顯式導入cPickle替代pickle,以大幅提升速度;其主要的限制是,你不能繼承該版本的Pickle和Unpickle,因爲它們是函數,而不是類(多數程序並不要求它們是類)。pickle和cPickle模塊使用兼容的數據格式,所以它們可以互換使用。

如果您的Python中有shelve模塊,它會自動選用cPickle模塊,而不是pickle,以達到更快的序列化。我還沒有解釋過shelve,但我馬上就會講到它。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章