首頁  >  文章  >  後端開發  >  Python 物件序列化與反序列化:第 2 部分

Python 物件序列化與反序列化:第 2 部分

PHPz
PHPz原創
2023-09-03 20:33:051438瀏覽

Python 对象序列化和反序列化:第 2 部分

這是關於序列化和反序列化 Python 物件的教程的第二部分。在第一部分中,您學習了基礎知識,然後深入研究了 Pickle 和 JSON 的細節。

在這一部分中,您將探索 YAML(確保擁有第一部分中的運行範例),討論效能和安全注意事項,了解其他序列化格式,最後了解如何選擇正確的方案。 p>

#YAML

YAML 是我最喜歡的格式。它是一種人性化的資料序列化格式。與 Pickle 和 JSON 不同,它不是 Python 標準庫的一部分,因此您需要安裝它:

pip 安裝 yaml

yaml模組只有load()dump()函數。預設情況下,它們使用像loads()dumps() 這樣的字串,但可以採用第二個參數,它是一個開放流,然後可以轉儲/加載到/來自文件。

import yaml



print yaml.dump(simple)



boolean: true

int_list: [1, 2, 3]

none: null

number: 3.44

text: string

請注意 YAML 與 Pickle 甚至 JSON 相比的可讀性。現在是 YAML 最酷的部分:它理解 Python 物件!無需自訂編碼器和解碼器。以下是使用 YAML 的複雜序列化/反序列化:

> serialized = yaml.dump(complex)

> print serialized



a: !!python/object:__main__.A

  simple:

    boolean: true

    int_list: [1, 2, 3]

    none: null

    number: 3.44

    text: string

when: 2016-03-07 00:00:00



> deserialized = yaml.load(serialized)

> deserialized == complex

True

如您所見,YAML 有自己的符號來標記 Python 物件。輸出仍然非常容易閱讀。日期時間物件不需要任何特殊標記,因為 YAML 本質上支援日期時間物件。

效能

在開始考慮效能之前,您需要考慮效能是否是一個問題。如果您相對不頻繁地序列化/反序列化少量資料(例如在程式開始時讀取設定檔),那麼效能並不是真正的問題,您可以繼續前進。

但是,假設您分析了系統並發現序列化和/或反序列化導致效能問題,則需要解決以下問題。

效能有兩個面向:序列化/反序列化的速度有多快,以及序列化表示有多大?

為了測試各種序列化格式的效能,我將建立一個較大的資料結構,並使用 Pickle、YAML 和 JSON 進行序列化/反序列化。 big_data 清單包含 5,000 個複雜物件。

big_data = [dict(a=simple, when=datetime.now().replace(microsecond=0)) for i in range(5000)]

泡菜

我將在這裡使用 IPython,因為它有方便的 %timeit 魔術函數來測量執行時間。

import cPickle as pickle



In [190]: %timeit serialized = pickle.dumps(big_data)

10 loops, best of 3: 51 ms per loop



In [191]: %timeit deserialized = pickle.loads(serialized)

10 loops, best of 3: 24.2 ms per loop



In [192]: deserialized == big_data

Out[192]: True



In [193]: len(serialized)

Out[193]: 747328

預設pickle需要83.1毫秒進行序列化,29.2毫秒進行反序列化,序列化大小為747,328位元組。

讓我們嘗試使用最高協定。

In [195]: %timeit serialized = pickle.dumps(big_data, protocol=pickle.HIGHEST_PROTOCOL)

10 loops, best of 3: 21.2 ms per loop



In [196]: %timeit deserialized = pickle.loads(serialized)

10 loops, best of 3: 25.2 ms per loop



In [197]: len(serialized)

Out[197]: 394350

有趣的結果。序列化時間縮短至僅 21.2 毫秒,但反序列化時間略為增加,達 25.2 毫秒。序列化大小顯著縮小至 394,350 位元組 (52%)。

JSON

In [253] %timeit serialized = json.dumps(big_data, cls=CustomEncoder)

10 loops, best of 3: 34.7 ms per loop



In [253] %timeit deserialized = json.loads(serialized, object_hook=decode_object)

10 loops, best of 3: 148 ms per loop



In [255]: len(serialized)

Out[255]: 730000

好的。編碼方面的表現似乎比 Pickle 差一點,但解碼的表現卻差很多很多:慢了 6 倍。這是怎麼回事?這是 object_hook 函數的一個工件,需要為每個字典執行以檢查是否需要將其轉換為物件。不使用物件掛鉤運行速度要快得多。

%timeit deserialized = json.loads(serialized)

10 loops, best of 3: 36.2 ms per loop

這裡的教訓是,在序列化和反序列化為 JSON 時,請仔細考慮任何自訂編碼,因為它們可能會對整體效能產生重大影響。

YAML

In [293]: %timeit serialized = yaml.dump(big_data)

1 loops, best of 3: 1.22 s per loop



In[294]: %timeit deserialized = yaml.load(serialized)

1 loops, best of 3: 2.03 s per loop



In [295]: len(serialized)

Out[295]: 200091

好的。 YAML 真的非常非常慢。但是,請注意一些有趣的事情:序列化大小僅為 200,091 位元組。比 Pickle 和 JSON 都好得多。讓我們快速了解內部:

In [300]: print serialized[:211]

- a: &id001

    boolean: true

    int_list: [1, 2, 3]

    none: null

    number: 3.44

    text: string

  when: 2016-03-13 00:11:44

- a: *id001

  when: 2016-03-13 00:11:44

- a: *id001

  when: 2016-03-13 00:11:44

YAML 在這裡非常聰明。它確定所有 5,000 個字典共享相同的“a”鍵值,因此它僅存儲一次並使用 *id001 為所有物件引用它。

安全性

安全性通常是一個關鍵問題。 Pickle和YAML由於建構Python對象,很容易受到程式碼執行攻擊。格式巧妙的檔案可以包含將由 Pickle 或 YAML 執行的任意程式碼。無需驚慌。這是設計使然,並記錄在 Pickle 的文檔中:

警告:pickle 模組並非旨在防止錯誤或惡意建構的資料。切勿取消從不受信任或未經身份驗證的來源收到的資料。

以及 YAML 文件中的內容:

警告:使用從不受信任的來源收到的任何資料呼叫 yaml.load 是不安全的! yaml.load 與 pickle.load 一樣強大,因此可以呼叫任何 Python 函數。

您只需要了解,不應使用 Pickle 或 YAML 載入從不受信任的來源收到的序列化資料。 JSON 沒問題,但是如果您有自訂編碼器/解碼器,那麼您也可能會暴露。

yaml 模組提供了 yaml.safe_load() 函數,該函數僅加載簡單的對象,但隨後您會失去很多 YAML 的功能,並且可能選擇只使用 JSON。

其他格式

還有許多其他可用的序列化格式。以下是其中的一些。

協定緩衝區

Protobuf(即協定緩衝區)是 Google 的資料交換格式。它是用 C 實現的,但具有 Python 綁定。它具有複雜的架構並有效地打包資料。非常強大,但不太容易使用。

訊息包

MessagePack 是另一種流行的序列化格式。它也是二進制且高效的,但與 Protobuf 不同的是它不需要模式。它有一個類似於 JSON 的類型系統,但更豐富一些。鍵可以是任何類型,不僅支援字串和非 UTF8 字串。

CBOR

CBOR 代表簡潔二進位物件表示。同樣,它支援 JSON 資料模型。 CBOR 不像 Protobuf 或 MessagePack 那麼出名,但它很有趣,原因有兩個:

  1. 它是官方網路標準:RFC 7049。
  2. 它是專為物聯網 (IoT) 設計。

如何選擇?

這是一個大問題。這麼多選擇,你如何選擇?讓我們考慮一下應該考慮的各種因素:

  1. 序列化格式應該是人類可讀和/或人類可編輯的嗎?
  2. 是否會從不受信任的來源接收序列化內容?
  3. 序列化/反序列化是效能瓶頸嗎?
  4. 序列化資料是否需要與非Python環境交換?

我會讓您變得非常簡單,並介紹幾種常見場景以及我為每種場景推薦的格式:

自動儲存Python程式的本機狀態

此處使用 pickle (cPickle) 和 HIGHEST_PROTOCOL。它快速、高效,無需任何特殊程式碼即可儲存和載入大多數 Python 物件。它也可以用作本地持久緩存。

設定檔

絕對是 YAML。對於人類需要閱讀或編輯的任何內容來說,沒有什麼比它的簡單性更好的了。它已被 Ansible 和許多其他專案成功使用。在某些情況下,您可能會喜歡使用直接的 Python 模組作為設定檔。這可能是正確的選擇,但它不是序列化,它實際上是程式的一部分,而不是單獨的設定檔。

Web API

JSON 顯然是這裡的贏家。如今,Web API 最常由原生使用 JSON 的 JavaScript Web 應用程式使用。某些Web API 可能會傳回其他格式(例如,用於密集表格結果集的csv),但我認為您可以以最小的開銷將csv 資料打包為JSON(無需將每一行作為具有所有列名稱的物件重複)。

高容量/低延遲大規模通訊

使用二進位協定之一:Protobuf(如果需要架構)、MessagePack 或 CBOR。執行您自己的測試來驗證每個選項的效能和代表能力。

結論

Python物件的序列化和反序列化是分散式系統的一個重要面向。您無法直接透過網路傳送 Python 物件。您經常需要與其他語言實作的其他系統進行互通,有時您只想將程式的狀態儲存在持久性儲存中。

Python 在其標準庫中附帶了多種序列化方案,還有更多序列化方案可作為第三方模組使用。了解所有選項以及每個選項的優缺點將使您能夠選擇最適合您情況的方法。

以上是Python 物件序列化與反序列化:第 2 部分的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn