pickle 更快的數(shù)據(jù)儲存方式
在之前的python系列推文中,我們介紹了文件讀寫系列函數(shù),也介紹了諸多python中的原生數(shù)據(jù)類型,這是每一個學(xué)習(xí)python語言的人都應(yīng)該掌握的知識。而如果你正逐漸將自己的數(shù)據(jù)處理工作轉(zhuǎn)移到python上來,就會面臨一個首要的問題————如何妥善保存python中產(chǎn)生的數(shù)據(jù),如一個列表,若干個字典。很多人會選擇將數(shù)據(jù)寫入一個文本文檔,使用時再將之讀入。這固然是一種解決辦法,但不夠“優(yōu)雅”,今天我們將會介紹一種優(yōu)雅的python讀寫數(shù)據(jù)對象的方式:pickle模塊
對象的序列化(serialization)與反序列化(deserialization)
我們曾經(jīng)提到,python中的所有數(shù)據(jù)類型(如列表、字典)的實(shí)例化(一個列表a = [1,2,3] ,一個字典dicta = {'name':'tom'})都可看作是一個對象(object),對象通常是一種邏輯上的實(shí)體,比如一個班級名單列表,以一個list的形式給出,則它是一個對象,一個摩爾斯電碼詞典,以一個dict的形式給出,則它也是一個對象,當(dāng)我們想將一個列表中的信息存儲下來,以備下次工作時使用時,我們也許會用文件讀寫函數(shù)來完成,如:
- classlist = ['tom','karry','bob']
- with open("classlist.txt","w+") as f:
- for name in classlist:
- f.write(name+'\n')
通過使用讀寫函數(shù),我們將該列表的內(nèi)容分行寫入一個名為classlist的列表中。當(dāng)下次使用時再分行讀入。
但python存在著專門讀寫python對象的方法,那就是pickle模塊,用于解決對象的序列化與反序列化。所謂序列化指的是將python的對象轉(zhuǎn)化為一種專門的二進(jìn)制字符串,并將其以二進(jìn)制形式寫入一個文件中,而反序列化則是讀取該二進(jìn)制文件并將其轉(zhuǎn)化回對象本身。這樣子做有什么好處呢?最顯而易見的是它的存儲與讀寫速度會非常的快,后面我們會給出示例說明。其次創(chuàng)立專門的對象數(shù)據(jù)文件,可以防止數(shù)據(jù)被不小心修改,產(chǎn)生問題。
pickle模塊在標(biāo)準(zhǔn)庫內(nèi),引入pickle模塊很簡便。
- import pickle
pickle dump
將對象轉(zhuǎn)化為二進(jìn)制存儲文件使用的是pickle模塊的dump方法,現(xiàn)在我們將一個長度為100的列表序列化并保存。
- import random
- import pickle
- a = [random.random() for x in range(100)]
- with open("alist.pkl",'wb') as f:
- pickle.dump(a,f)
可以看到,與一般的文件讀寫不同的是,我們的讀寫方式為wb,也就是二進(jìn)制方式書寫,pickle.dump方法接收兩個必需的參數(shù),***個是要序列化的對象,第二個是二進(jìn)制文件句柄。執(zhí)行完畢后,當(dāng)前工作目錄會多出來一個名為alist.pkl的文件,需要說明的是,pkl只是一個形式上的后綴名,你可以寫成任何其它字符,但為了顯示它是一個pickle序列化的數(shù)據(jù)對象,我們使用pkl作為標(biāo)記。
pickle load
有讀入就會有讀取,將pkl文件讀取為對象使用的是pickle模塊的dump方法,現(xiàn)在我們將剛剛存儲的數(shù)據(jù)文件讀取
- with open("alist.pkl",'rb') as f:
- abak = pickle.load(f)
通過這個樣子即可反序列化pkl文件,變成python的列表對象。
更快的速度與更小的體積
在***節(jié)中我們提到,使用pickle來讀寫對象,其效率更高,我們使用time模塊驗(yàn)證一下
- In [27]: import time
- In [28]: a = [random.random() for x in range(100000)]
- In [29]: with open("filewrite.txt",'w') as f:
- ...: starttime = time.time()
- ...: for num in a:
- ...: f.write(str(num)+'\n')
- ...: filetime = time.time() - starttime
- ...:
- In [30]: filetime
- Out[30]: 0.32804441452026367
- In [31]: with open("pklwrite.pkl",'wb') as f:
- ...: starttime = time.time()
- ...: pickle.dump(a,f)
- ...: pkltime = time.time() - starttime
- ...:
- In [32]: pkltime
- Out[32]: 0.10946011543273926
可以看到,對于同樣讀寫一個長度為100000的列表對象,使用文件讀寫函數(shù)所花的時間是使用了pickle模塊的三倍多,當(dāng)文件對象越大,差距也會越大。
我們可以查看兩個文件的大小,filewrite.txt 的大小為1980kb,而pklwrite.pkl僅有880kb,在分別打包為rar文件后,大小分別為863kb與780kb,這說明序列化對象同樣減少了數(shù)據(jù)存儲的空間,使用pickle模塊存儲python數(shù)據(jù)對象是既省時間又省空間的更優(yōu)雅的方法。