この記事はTakumi Akashiro ひとり Advent Calendar 2020の16日目の記事です。
遂に数日分あった当アドカレのストックも切れ、
朝に事前投稿できなくなるほど、締め切りに追われるようになりました!
追い込まれている感じが楽しいですね!
始めに
みなさまはPythonのオブジェクトを一時保存したいと思ったことはないでしょうか?
例えば、デバッグとか……いや、今はDCCツールならVSCodeでptvsd経由でデバッグするのがメジャー1ですもんね……
例えば、Mayaで使った複雑な構造の変数を他プロセスのPythonで使いたいとか……これはギリギリありそうですね。
今日はこれを想定にやっていきましょう!
シリアライズ とは
オブジェクト指向プログラミングにおいて、あるオブジェクトをファイルとして書き出すことをシリアライズ、と呼ぶそうです。
Wikipedia曰くは以下のように書かれています。
シリアライズ - Wikipedia
第二の意味の直列化(ちょくれつか)は、オブジェクト指向プログラミングにおいて使われる用語で、ある環境において存在しているオブジェクトを、バイト列やXMLフォーマットに変換することをいう。より具体的には、そのオブジェクトの状態を表す変数(フィールド)と、場合によってはオブジェクトの種類(クラス)を表すなんらかの識別子を、ファイル化できるようなバイト列やXMLフォーマットの形に書き出すことをいう。これにより、オブジェクトの表すデータを、ファイルとしてセーブしたり、ネットワークで送信したりすることができるようになる。このようにして得られたバイト列やXMLフォーマットは、直列化復元ないしデシリアライズによって、元のオブジェクトに復元される。また、オブジェクトを直列化してファイルなどの永続記憶に保存することを永続化という。`
…自分は単語自体は知っていましたが、今日の記事のために調べて、初めてオブジェクト指向の用語ってのは知りましたね。
そしてPythonでserializeを行ってくれる標準モジュールは……pickle
です!!
とりあえず使っていきましょう!
pickle
を使ってみる
まずは保存してみましょう!
#! python3.8
import pickle
import tempfile
import pathlib
import re
data = {"nemui": "nero", "ohayou": "oyasumi", "nanka": "kanka", "hoge": pathlib.Path("d:/"), "reg": re.compile("\D{2}\d{16}")}
with open(pathlib.Path(tempfile.gettempdir()) / 'sample.pickle', 'wb') as f:
pickle.dump(data, f)
#! python3.8
import pickle
import tempfile
import pathlib
with open(pathlib.Path(tempfile.gettempdir()) / 'sample.pickle', 'rb') as f:
data = pickle.load(f)
for k, v in data.items():
print(f"\t{k}:\t{v} ({type(v)})")
ちゃんと変数を復元できていますし、Python3のdictの実装通りに順番も保持されてますね!
(実装側の都合で順番が保持される特性がついたわけだから、当然か……)
Python3 で出力した pickle をPython2で読んでみる
さあ、これをPython2に持っていってみましょう!
アッ……今の私用PCにPython2が入ってねえ!
tempの位置が違うんか、お前……
と茶番(?)はさておき、気を取り直して、今度こそPython2で実行してみましょう。
#! python2
import pickle
import tempfile
with open(tempfile.gettempdir() + '/sample.pickle', 'rb') as f:
data = pickle.load(f)
for k, v in data.items():
print("\t{0}:\t{1} ({2})".format(k, v, type(v)))
「プロトコル ガ チガイマス」 とエラーが出てきます!
やったね!(?)
公式ドキュメントを読んでみる
困ったときは公式ドキュメントを読んでみましょう!
pickle --- Python オブジェクトの直列化 — Python 3.9.1 ドキュメント
現在、pickle
に使用できるプロトコルは6種類あります。
使用されるプロトコルが高ければ高いほど、生成されたpickle
を読むために必要なPythonの最新バージョンが必要になります。
- プロトコルバージョン3はPython 3.0で追加されました。
これはbyteオブジェクトを明示的にサポートしており、Python 2.xではpickleを解除できません。
- プロトコルバージョン4はPython 3.4で追加されました。
非常に大きなオブジェクトのサポート、より多くの種類のオブジェクトのピックリング、いくつかのデータフォーマットの最適化が追加されました。これはPython 3.8から始まるデフォルトのプロトコルです。プロトコル4による改善点についてはPEP 3154を参照してください。
- プロトコルバージョン 5 は Python 3.8 で追加されました。
帯域外データのサポートと帯域内データの高速化が追加されました。プロトコル 5 の改良点については、PEP 574 を参照してください。
(中略)
pickle.dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None)
というわけなので、pickle(data, f, protocol=2)
と置き換えると……
アッ…そうだ…pathlibはPython2系にはなかった……
仕方ないので、pathlibにしてたパスをstringに置き換えると
python3の変数も無事、Python2系で読めましたね!
(もうPython2とか使わなくなるのに、このくだり要る?)
締め
自分もごくまれにしか使わないですが、まあまあ便利なのでお勧めです。
まあ、簡単な構造の変数だったり、速度を犠牲にしていいのであれば、
個人的には可読性の高いjsonの方が好みですけど。
トラブったときに対応しやすいですし。
-
今年はほぼPythonに向き合ってないので、この方法すら時代遅れなのかもしれない……と恐怖しながら書いてます。 ↩