Python 序列化(Python IO)

veelion發表於2019-03-03

上一節我們學習了檔案的讀寫,把一個字串(或位元組物件)儲存到磁碟是一件很容易的事情。但是在實際程式設計中,我們經常需要儲存結構化資料,比如複雜的字典、巢狀的列表等等,這時候就需要我們想辦法把這些結構化資料先轉變成一個字串,這個轉換過程就叫做“序列化”,這一過程的逆操作就是“反序列化”。

Python序列化

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可以作為一個選擇進行復雜物件的序列化。

猿人學banner宣傳圖

我的公眾號:猿人學 Python 上會分享更多心得體會,敬請關注。

***版權申明:若沒有特殊說明,文章皆是猿人學 yuanrenxue.com 原創,沒有猿人學授權,請勿以任何形式轉載。***

相關文章