Python 好用套件介紹 - cloudpickle (pickle 模組的鋼鐵裝)
Posted on Sep 3, 2023 in Python 模組/套件推薦 , Python 程式設計 - 中階 by Amo Chen ‐ 2 min read
你有沒有遇過某些資料或類別 pickle 之後,之後要 unpickle 時出現 AttributeError
的情況,例如:
AttributeError: Can't get attribute 'A' on <module '__main__' (built-in)>
這是由於 pickle 使用的是 serialization by reference 技術,所以某些資料或類別它不會放到序列化的結果,因此這種問題可以試看看用 cloudpickle 解決。
一起看看 cloudpickle 與 pickle 模組之間的差異,以及它如何能解決你的問題吧!
本文環境
- Python 3
- cloudpickle
$ pip install cloudpickle
cloudpickle 簡介
cloudpickle 是 Python pickle 模組的擴充,可以將原本 pickle 無法序列化(serialize)的 lambda, function 等給序列化,因爲 pickle 模組是 serialization by reference, 簡單來說就是告訴 Python 這個序列化後的東西有用到什麼 class, function 而已,沒有把這些 class 與 function 一併序列化,所以 A 機器序列化的結果,丟到 B 機器執行會有問題,因為沒有當初那些 class 或 function 。
例如 A 機器序列化 class A
:
>>> class A:
... pass
...
>>> pickle.dumps(A)
b'\x80\x04\x95\x12\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x01A\x94\x93\x94.'
接著,丟到 B 機器 unpicklize:
>>> pickle.loads(b'\x80\x04\x95\x12\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x01A\x94\x93\x94.')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Can't get attribute 'A' on <module '__main__' (built-in)>
但如果改成用 cloudpickle 就不會這樣了!
A 機器序列化 class A
, 此處可以看到序列化的結果比較長,這是因為 class A
的定義也被序列化:
>>> import cloudpickle
>>> class A:
... pass
...
>>> cloudpickle.dumps(A)
b'\x80\x05\x95\xee\x00\x00\x00\x00\x00\x00\x00\x8c\x17cloudpickle.cloudpickle\x94\x8c\x14_make_skeleton_class\x94\x93\x94(\x8c\x08builtins\x94\x8c\x04type\x94\x93\x94\x8c\x01A\x94h\x03\x8c\x06object\x94\x93\x94\x85\x94}\x94\x8c\n__module__\x94\x8c\x08__main__\x94s\x8c 8fbd6d3daf3e404691dcd99c7bc8829f\x94Nt\x94R\x94\x8c\x1ccloudpickle.cloudpickle_fast\x94\x8c\x0f_class_setstate\x94\x93\x94h\x0f}\x94(h\x0bh\x0c\x8c\x07__doc__\x94Nu}\x94\x86\x94\x86R0.'
B 機器 unpickle:
>>> import cloudpickle
>>> cloudpickle.loads(b'\x80\x05\x95\xee\x00\x00\x00\x00\x00\x00\x00\x8c\x17cloudpickle.cloudpickle\x94\x8c\x14_make_skeleton_class\x94\x93\x94(\x8c\x08builtins\x94\x8c\x04type\x94\x93\x94\x8c\x01A\x94h\x03\x8c\x06object\x94\x93\x94\x85\x94}\x94\x8c\n__module__\x94\x8c\x08__main__\x94s\x8c 8fbd6d3daf3e404691dcd99c7bc8829f\x94Nt\x94R\x94\x8c\x1ccloudpickle.cloudpickle_fast\x94\x8c\x0f_class_setstate\x94\x93\x94h\x0f}\x94(h\x0bh\x0c\x8c\x07__doc__\x94Nu}\x94\x86\x94\x86R0.')
<class '__main__.A'>
一點問題也沒有!妥妥的!
Serialization by Value
由於 cloudpickle 用的是 serialization by value, 用到的 function, class 等定義都會一併序列化,所以不會有 reference 找不到的問題,但是 import 的部分不會做序列化,因為如果連 import 的模組都要序列化的話,就會跟粽子一樣一大串,恐怕失去效率。
這種序列化技術相當適合類似 Spark 這種叢集運算(cluster computing), 只要將程式序列化之後,就可以透過網路傳輸到各個節點進行運算,所以你不用為了算數據,每個節點都需要部署相同的程式,只要確保相同的執行環境(Python 版本、套件版本等等)就好,剩下的交給 cloudpickle 這類技術。
噢,沒錯, PySpark 內部就是使用 cloudpickle XD
最後宣導一個資安觀念,千萬別隨意執行未知來源的 pickle data !
以上!
Happy Coding!
References
https://github.com/cloudpipe/cloudpickle
pickle — Python object serialization