pickle --- Python 物件序列化¶
原始碼:Lib/pickle.py
pickle 模組實作的是一個在二進位層級上對 Python 物件進行序列化(serialize)或去序列化(de-serialize)。"Pickling" 用於專門指摘將一個 Python 物件轉換為一個二進位串流的過程,"unpickling" 則相反,指的是將一個(來自 binary file 或 bytes-like object 的)二進位串流轉換回 Python 物件的過程。Pickling(和 unpickling)的過程也可能被稱作 "serialization", "marshalling," [1] 或 "flattening"。不過,為了避免混淆,本文件將統一稱作封裝(pickling)、拆封(unpickling)。
警告
pickle 模組並不安全,切記只拆封你信任的資料。
pickle 封包是有可能被建立來在拆封的時候執行任意惡意程式碼的。絕對不要拆封任何你無法信任其來源、或可能被修改過的 pickle 封包。
建議你可以使用 hmac 模組來簽署這個封包,以確保其未被修改過。
如果你在處理不受信任的資料,其他比較安全的序列化格式(例如 json)可能會更適合。請參照 See 和 json 的比較 的說明。
和其他 Python 模組的關係¶
和 marshal 的比較¶
Python 有另一個比較原始的序列化模組叫 marshal,不過其設計目的是為了支援 Python 的預編譯功能 .pyc 的運作。總地來說,請盡可能地使用 pickle,沒事不要用 marshal。
pickle 和 marshal 有幾個明顯不同的地方:
marshalcannot be used to serialize user-defined classes and their instances.picklecan save and restore class instances transparently, however the class definition must be importable and live in the same module as when the object was pickled.marshal序列化格式無法保證能在不同版本的 Python 之間移植。因為其主要的作用是支援.pyc檔案的運作,Python 的實作人員會在需要時實作無法前向相容的序列化方式。但只要選擇了相容的 pickle 協定,且處理了 Python 2 和 Python 3 之間的資料類型差異,pickle序列化協定能保證在不同 Python 版本間的相容性。
和 json 的比較¶
pickle 協定和 JSON (JavaScript Object Notation) 有一些根本上的不同:
JSON 以文字形式作為序列化的輸出(輸出 unicode 文字,但大多數又會被編碼為
UTF-8),而 pickle 則是以二進位形式作為序列化的輸出;JSON 是人類可讀的,而 pickle 則無法;
JSON 具有高互通性(interoperability)且在 Python 以外的環境也被大量利用,但 pickle 只能在 Python 內使用。
預設狀態下的 JSON 只能紀錄一小部份的 Python 內建型別,且無法紀錄自訂類別;但透過 Python 的自省措施,pickle 可以紀錄絕大多數的 Python 型別(其他比較複雜的狀況也可以透過實作 specific object APIs 來解決);
去序列化不安全的 JSON 不會產生任意程式執行的風險,但去序列化不安全的 pickle 會。
也參考
json module: 是標準函式庫的一部分,可讓使用者進行 JSON 的序列化與去序列化。
資料串流格式¶
pickle 使用的資料格式是針對 Python 而設計的。好處是他不會受到外部標準(像是 JSON,無法紀錄指標共用)的限制;不過這也代表其他不是 Python 的程式可能無法重建 pickle 封裝的 Python 物件。
以預設設定來說,pickle 使用相對緊湊的二進位形式來儲存資料。如果你需要盡可能地縮小檔案大小,你可以壓縮封裝的資料。
pickletools 含有工具可分析 pickle 所產生的資料流。pickletools 的源始碼詳細地記載了所有 pickle 協定的操作碼(opcode)。
截至目前為止,共有六種不同版本的協定可用於封裝 pickle。數字越大版本代表你需要使用越新的 Python 版本來拆封相應的 pickle 封裝。
版本 0 的協定是最初「人類可讀」的版本,且可以向前支援早期版本的 Python。
版本 1 的協定使用舊的二進位格式,一樣能向前支援早期版本的 Python。
版本 2 的協定在 Python 2.3 中初次被引入。其可提供更高效率的 new-style classes 封裝過程。請參閱 PEP 307 以了解版本 2 帶來的改進。
版本 3 的協定在 Python 3.0 被新增。現在能支援封裝
bytes的物件且無法被 2.x 版本的 Python 拆封。在 3.0~3.7 的 Python 預設使用 3 版協定。版本 4 的協定在 Python 3.4 被新增。現在能支援超大物件的封裝、更多種型別的物件以及針對部份資料格式的儲存進行最佳化。從 Python 3.8 到 3.13,預設使用第 4 版協定。請參閱 PEP 3154 以了解第 4 版協定改進的細節。
版本 5 的協定在 Python 3.8 被新增。現在能支援帶外資料(Out-of-band data)並加速帶內資料的處理速度。自 3.14 起這是預設使用的協定。請參閱 PEP 574 以了解第 5 版協定改進的細節。
備註
資料序列化是一個比資料持久化更早出現的概念;雖然 pickle 可以讀寫檔案物件,但它並不處理命名持久物件的問題,也不處理對持久物件的並行存取、一個更棘手的問題。pickle 模組可以將複雜物件轉換成位元組串流,也可以將位元組串流轉換回具有相同原始內部結構的物件。對這些位元組串流最常見的處理方式是將它們寫入檔案中,但也可以將它們透過網路傳送或儲存在一個資料庫中。shelve 模組提供了一個簡單的介面來讓使用者在 DBM 風格的資料庫檔案中對物件進行封裝和拆封的操作。
模組介面¶
想要序列化一個物件,你只需要呼叫 dumps() 函式。而當你想要去序列化一個資料流時,你只需要呼叫 loads() 即可。不過,若你希望能各自對序列化和去序列化的過程中有更多的掌控度,你可以自訂一個 Pickler 或 Unpickler 物件。
pickle 模組提供以下常數:
- pickle.DEFAULT_PROTOCOL¶
一個整數,用於 pickle 的預設 協定版本。可能小於
HIGHEST_PROTOCOL。目前預設協定是 5,在 Python 3.8 中引入,與之前的版本不相容。此版本引入了對 out-of-band buffer 的支援,其中 PEP 3118 相容的資料可以與主要 pickle 串流分開傳輸。在 3.0 版的變更: 預設協定版本為 3。
在 3.8 版的變更: 預設協定版本為 4。
在 3.14 版的變更: 預設協定是 5。
pickle 模組提供下列函式來簡化封裝的過程:
- pickle.dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None)¶
將被封裝成 pickle 形式的物件 obj 寫入到已開啟的file object file。這等效於
Pickler(file, protocol).dump(obj)。引數 file、protocol、fix_imports 和 buffer_callback 的意義與
Pickler建構式中的相同。在 3.8 版的變更: 新增 buffer_callback 引數。
- pickle.dumps(obj, protocol=None, *, fix_imports=True, buffer_callback=None)¶
將被封裝為 pickle 形式的物件 obj 以
bytes類別回傳,而非寫入進檔案。引數 protocol、fix_imports 和 buffer_callback 的意義和
Pickler建構式中的相同。在 3.8 版的變更: 新增 buffer_callback 引數。