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()會從文件中讀取內容進行反序列化。
2.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}
>>> id(p)==id(p_dict)
False
2.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中是個函數的區別是一樣的。
3.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}
3.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內置的數據類型對應如下:
5 總結
(1)序列化與反序列化是為了解決內存中對象的持久化與傳輸問題;
(2)Python中提供了pickle和json兩個模塊進行序列化與反序列化;
(3)dumps()和dump()用於序列化,loads()和load()用於反序列化;
(4)pickle模塊能序列化任何對象,序列化結果為bites類型,只適合於Python機器之間交互;
json模塊只能序列化Python基本類型,序列化結果為json格式字符串,適合不同開發語言之間交互。