Python開發之序列化與反序列化:pickle、son模組使用詳解
1 引言
2 pickle模組
2.1 dumps()與loads()
2.2 dump()與load()
3 json模組
3.1 dumps()與loads()
3.2 dump()與load()
4 pickle與json的對比
5 總結
1 引言
在日常開發中,所有的物件都是儲存在記憶體當中,尤其是像python這樣的堅持一切接物件的高階程式設計語言,一旦關機,在寫在記憶體中的資料都將不復存在。另一方面,儲存在記憶體夠中的物件由於程式語言、網路環境等等因素,很難在網路中進行傳輸互動。由此,就誕生了一種機制,可以實現記憶體中的物件與方便持久化在磁碟中或在網路中進行互動的資料格式(str、bites)之間的相互轉換。這種機制就叫序列化與發序列化:
序列化:將記憶體中的不可持久化和傳輸物件轉換為可方便持久化和傳輸物件的過程。
反序列化:將可持久化和傳輸物件轉換為不可持久化和傳輸物件的過程。
Python中提供pickle和json兩個模組來實現序列化與反序列化,pickle模組和json模組dumps()、dump()、loads()、load()這是個函式,其中dumps()、dump()用於實現序列化,loads()、load()用於實現反序列化。下面,我們分別對pickle和json模組進行介紹。
2 pickle模組
pickle模組的dumps()、dump()、loads()、load()是個函式按功能劃分可以分為兩組:
序列化:dumps()、dump()
反序列化:loads()、load()
dumps()與dump()的區別是dumps()只是單純得將物件序列化,而dump()會在序列化之後將結果寫入到檔案當中;與之對應,loads()與load()區別至於loads()是對dumps的序列化結果進行反序列化,而dump()會從檔案中讀取內容進行反序列化。
(1)dumps()與loads()
>>> import pickle >>> p_dict = {'name':'張三' , 'age':30 , 'isMarried':False} # 定義一個字典 >>> p_str = pickle.dumps(p_dict) # 序列化 >>> type(p_dict) <class 'dict'> >>> type(p_str) <class 'bytes'> >>> p_str b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x06\x00\x00\x00\xe5\xbc\xa0\xe4\xb8\x89q\x02X\x03\x00\x00\x00ageq\x03K\x1eX\t\x00\x00\x00isMarriedq\x04\x89u.' >>> p = pickle.loads(p_str) >>> type(p) <class 'dict'> >>> p {'name': '張三', 'age': 30, 'isMarried': False}可以看到,反序列化後得到的p和序列化之前的p_dict內容是一模一樣的。不過,p與p_dict已經是兩個不同的物件了:
>>> id(p)==id(p_dict) False
(2)dump()與load()
序列化:
>>> import pickle >>> p_dict = {'name':'張三' , 'age':30 , 'isMarried':False} # 定義一個字典 >>> file = open("my_dump.txt", "wb") # 因為序列化只有的是bites型別,所以必須以wb模式開啟 >>> pickle.dump(p_dict, file) >>> file.close()
此時,通過上面程式碼我們已經將p_dict序列化成功,並寫入到了一個名為my_dump.txt檔案中。你可以找到這個檔案,然後將它拷貝到任何電腦上進行反序列化:
>>> file=open("my_dump.txt","rb") >>> p=pickle.load(file) >>> file.close() >>> type(p) <class 'dict'> >>> p {'name': '張三', 'age': 30, 'isMarried': False}
看,反序列化後得到的內容與序列化之前的內容完全一樣。體會到序列化與反序列化的作用了嗎?序列化之後的內容可以方便得儲存到磁碟中,電腦關機也不怕。
3 json模組
如果你閱讀並理解了上文中關於pickle的部門內容,對於這一部分的json模組內容,你可以不費吹灰之力掌握。上文中說到過,與pickle一樣,json模組也提供了dumps()、dump()、loads()、load()則是個函式,且其中區別也與pickle中是個函式的區別是一樣的。
(1)dumps()與loads()
>>> import pickle >>> p_dict = {'name':'張三' , 'age':30 , 'isMarried':False} # 定義一個字典 >>> import json >>> p_dict = {'name':'張三' , 'age':30 , 'isMarried':False} # 定義一個字典 >>> p_str = json.dumps(p_dict) >>> type(p_str) <class 'str'> >>> p_str '{"name": "\\u5f20\\u4e09", "age": 30, "isMarried": false}'
可以看到,json序列化之後得到的是json格式字串,但上述json字串中,中文部分內容顯示為了“亂碼”。怎麼辦呢?json的dumps()函式(dump()函式也有)中提供了一個ensure_ascii引數,將該引數的值設定為False,可令序列化後中文依然正常顯示。
>>> p_str2 = json.dumps(p_dict, ensure_ascii=False) >>> p_str2 '{"name": "張三", "age": 30, "isMarried": false}'
接著上面的內容進行反序列化:
>>> p1 = json.loads(p_str) >>> p1 {'name': '張三', 'age': 30, 'isMarried': False} >>> p2 = json.loads(p_str) >>> p2 {'name': '張三', 'age': 30, 'isMarried': False}
(2)dump()與load()
>>> import json >>> p_dict = {'name':'張三' , 'age':30 , 'isMarried':False} # 定義一個字典 >>> file = open('d:/mydump.txt' , 'w') >>> json.dump(p_dict , file) >>> file.close()
當然,你也可以加上ensure_ascii這一引數,並將其值設定為False,這樣你開啟mydump.txt檔案裡面的中文就能正常顯示。(執行完程式碼之後,本地會有一個mydump.txt檔案,諸位可以驗證該內容)
>>> file = open('d:/mydump.txt' , 'w') >>> json.dump(p_dict , file , ensure_ascii=False) >>> file.close()
繼續反序列化:
>>> file = open('d:/mydump.txt' , 'r') >>> p = json.load(file) >>> file.close() >>> type(p) <class 'dict'> >>> p {'name': '張三', 'age': 30, 'isMarried': False}
通過上面內容,pickle和json模組關於序列化與反序列化的操作就介紹完了。我們可以發現,pickle與json兩個模組無論是在函式名,還是在功能上,都是機器相似的。既然這樣,有了pickle模組,為什麼還有json模組的誕生呢?接下來來說說pickle與json模組的區別。
4 pickle模組與json木塊的區別
(1)pickle模組用於Python語言特有的型別和使用者自定義型別與Python基本資料型別之間的轉換
json模組用於字串和python資料型別間進行轉換。 如下所示,我們自定義一個Person類,分別用pickle和json進行序列化:
>>> class Person: def __init__(self , name , age , isMarried): self.name = name self.age = age self.isMarried = isMarried >>> p = Person('張三' , 30 , False)
使用pickle模組進行序列化與反序列化:
>>> p = Person('張三' , 30 , False) >>> import pickle >>> pp = pickle.dumps(p) >>> type(pp) <class 'bytes'> >>> pp b'\x80\x03c__main__\nPerson\nq\x00)\x81q\x01}q\x02(X\x04\x00\x00\x00nameq\x03X\x06\x00\x00\x00\xe5\xbc\xa0\xe4\xb8\x89q\x04X\x03\x00\x00\x00ageq\x05K\x1eX\t\x00\x00\x00isMarriedq\x06\x89ub.' >>> p2 = pickle.loads(pp) >>> type(p2) <class '__main__.Person'> >>> p2.name '張三'
甚至pickle模組還能夠對Peron本身進行序列化:
>>> per = pickle.dumps(Person) >>> per b'\x80\x03c__main__\nPerson\nq\x00.' >>> per2 = pickle.loads(per) >>> per2 <class '__main__.Person'>
如果用json對Person例項物件進行序列化,就會報錯:
>>> import json >>> p = Person('張三' , 30 , False) >>> json.dumps(p) Traceback (most recent call last): File "<pyshell#49>", line 1, in <module> json.dumps(p) …… TypeError: Object of type 'Person' is not JSON serializable
如果非要用json對Person物件進行序列化,必須先定義一個將Person物件轉化為字典(dict)的方法 :
>>> def person2dict(per): return { 'name':per.name , 'age':per.age , 'isMarried':per.isMarried } >>> p3 = json.dumps(p , default=person2dict) >>> type(p3) <class 'str'> >>> p3 '{"name": "\\u5f20\\u4e09", "age": 30, "isMarried": false}' >>> p3 = json.dumps(p , default=person2dict , ensure_ascii=False) >>> type(p3) <class 'str'> >>> p3 '{"name": "張三", "age": 30, "isMarried": false}'
當然,也不能直接進行反序列化,不然也只會得到一個字典:
>>> p4 = json.loads(p3) >>> type(p4) <class 'dict'> >>> p4 {'name': '張三', 'age': 30, 'isMarried': False}
此時,也要定義一個將字典轉換為Person類例項的方法,在進行反序列化:
>>> def dict2person(d): return Person(d['name'],d['age'],d['isMarried']) >>> p5 = json.loads(p3 , object_hook=dict2person) >>> type(p5) <class '__main__.Person'> >>> p5.name '張三'
(2)pickle序列化結果為bites型別,只適合於Python機器之間的互動。
json序列化結果為str型別,能夠被多種語言識別,可用於與其他程式設計語言互動。
目前,JSON格式字串已經成為網路傳輸中的一種標準格式,所以在web後臺開發中通常用json模組來序列化而不是pickle模組。
JSON和Python內建的資料型別對應如下:
JSON型別 |
Python型別 |
{} |
dict |
[] |
list |
"string" |
'str'或u'unicode' |
1234.56 |
int或float |
true/false |
True/False |
null |
None |
5 總結
(1)序列化與反序列化是為了解決記憶體中物件的持久化與傳輸問題;
(2)Python中提供了pickle和json兩個模組進行序列化與反序列化;
(3)dumps()和dump()用於序列化,loads()和load()用於反序列化;
(4)pickle模組能序列化任何物件,序列化結果為bites型別,只適合於Python機器之間互動;
json模組只能序列化Python基本型別,序列化結果為json格式字串,適合不同開發語言之間互動。
---恢復內容結束---