上一節我們學習了檔案的讀寫,把一個字串(或位元組物件)儲存到磁碟是一件很容易的事情。但是在實際程式設計中,我們經常需要儲存結構化資料,比如複雜的字典、巢狀的列表等等,這時候就需要我們想辦法把這些結構化資料先轉變成一個字串,這個轉換過程就叫做“序列化”,這一過程的逆操作就是“反序列化”。
JSON序列化
序列化資料的操作在各個語言程式設計中都會遇到,當然也出現了標準化的格式,比如:JSON(JavaScript Object Notation)。JSON格式通常被現代應用程式用於資料交換,尤其是在Web中廣為人知,是許多程式設計師的選擇。Python支援JSON的模組叫做json
。
JSON的資料格式和Python中的字典和列表非常相似,可以說它是字典和列表相互巢狀的結合體,而這些字典和列表的基本資料型別只能是:字串、整數、浮點數、布林型、None,不能是自定義的類等複雜物件。
一個物件x
可以用一行簡單的程式碼轉換成它對應的JSON字串:
In [124]: import json
In [125]: json.dumps({'Tom': 23, 'Jim': 25, 'William': 21})
Out[125]: '{"Tom": 23, "Jim": 25, "William": 21}'
把JSON字串反序列化為Python物件的程式碼也只有一行:
In [126]: json.loads('{"Tom": 23, "Jim": 25, "William": 21}')
Out[126]: {'Tom': 23, 'Jim': 25, 'William': 21}
dumps()
方法有個變體叫做dump()
,它是將物件序列化到檔案中。如果f
是一個檔案物件,我們可以這樣操作:
json.dump(x, f)
對應的,從檔案物件f
中反序列化的操作就是:
x = json.load(f)
dumps()
函式有很多引數可選,使我們生成不同格式的JSON字串,具體可以在IPython中通過json.dumps?
來檢視。我們可以通過下面的例子來了解一下:
(1)緊湊編碼
通過separators
引數來實現:
In [130]: json.dumps({"Tom": 23, "Jim": 25, "William": 21}, separators=(',', ':'))
Out[130]: '{"Tom":23,"Jim":25,"William":21}'
(2)美化輸出
通過sort_keys, indent
引數來實現:
In [132]: print(json.dumps({"Tom": 23, "Jim": 25, '9':3, '3': 10}, sort_keys=True, indent=4))
{
"3": 10,
"9": 3,
"Jim": 25,
"Tom": 23
}
(3)中文編碼
引數ensure_ascii
預設為True,會把中文等非ascii字元轉義:
In [133]: print(json.dumps({"小剛": 23, "小明": 25, '9':3, '3': 10}, sort_keys=True, indent=4))
{
"3": 10,
"9": 3,
"\u5c0f\u521a": 23,
"\u5c0f\u660e": 25
}
In [134]: print(json.dumps({"小剛": 23, "小明": 25, '9':3, '3': 10}, sort_keys=True, indent=4, ensure_ascii=False))
{
"3": 10,
"9": 3,
"小剛": 23,
"小明": 25
}
pickle模組序列化
與json
模組不同,pickle
可以對任意複雜的Python物件進行序列化,它是Python特有的,不能與其它語言進行通訊。預設情況下,它也是不安全的,如果資料是由黑客精心設計的,則反序列化的資料可能被植入惡意程式碼。
pickle
的介面跟json
是一樣的,序列化用dumps(x), dump(x, f)
,反序列化使用loads(s), load(f)
。但是,pickle
可以序列化任意複雜的物件,比如自定義的類、函式都是可以用它來序列化的。比如下面這個例子就是序列化b並反序列化一個函式:
In [136]: def add(x, y):
...: print(x+y)
...:
In [137]: import pickle
In [138]: s = pickle.dumps(add)
In [139]: s
Out[139]: b'\x80\x03c__main__\nadd\nq\x00.'
In [140]: f = pickle.loads(s)
In [141]: f
Out[141]: <function __main__.add(x, y)>
In [142]: f(2, 3)
5
從這個例子中,我們實現了序列化和反序列化一個函式的功能。這樣,我們可以把一些函式、自定義的類等各種物件先序列化到檔案,然後把這個檔案發給別人,別人可以通過反序列化來使用這些自定義的類和函式。這個過程中,如果有人對序列化檔案做了手腳,比如通過修改檔案修改了函式add()
的實現,就有可能被黑客利用來進行攻擊。這也是前面我們為什麼說pickle
預設是不安全的。所以,在選擇是否使用它進行序列化時,要先思考一番。
總結
Python為我們提供了資料序列化的工具。如果需要和其它程式進行資料交換,json是最好的選擇。如果是自己內部使用,pickle可以作為一個選擇進行復雜物件的序列化。
我的公眾號:猿人學 Python 上會分享更多心得體會,敬請關注。
***版權申明:若沒有特殊說明,文章皆是猿人學 yuanrenxue.com 原創,沒有猿人學授權,請勿以任何形式轉載。***