漬物は、食品を保存する方法としてよく知られている。
ウィキペディアによると、かなり古くから行われている方法で、漬物の起源は不明だが、おそらく4400年前に古代メソポタミア人がこの方法を用いていたと思われる。
特定の溶液に漬け込むことで、保存性を飛躍的に高めることができる。
つまり、食品を保存し、消費するための方法なのです。
もしあなたがPythonの開発者なら、ある日突然、Pythonのオブジェクトを後で使うために保存する方法を必要とすることに気づくかもしれません。
シリアル化
シリアライゼーションは、オブジェクトやデータ構造をバイトストリームや文字列に変換する処理です。
バイトストリームとは、1バイトが8ビットの0と1で構成されている、バイトのストリームのことです。
このバイトストリームは、保存や転送が簡単にできる。
これにより、開発者は、例えば、設定データやユーザーの進捗状況を保存し、それを(ディスクやデータベースに)保存したり、別の場所に送信したりすることができます。
PythonのオブジェクトはPickleと呼ばれるモジュールを使ってシリアライズすることもできます。
Pythonオブジェクトを漬けることと野菜を漬けることの大きな違いは、漬け物の味や食感が不可避かつ不可逆的に変化してしまうことです。
一方、Pythonオブジェクトのピクルスは、簡単に元の形に戻すことができます。
ところで、このプロセスは普遍的にデシリアライゼーションとして知られています。
ピクルス化(または一般的なシリアライズ)は圧縮と混同してはいけません。
ピックリングの目的は、データをRAMからディスクに転送できる形式に変換することです。
一方、圧縮は、より少ないビット数でデータをエンコードするプロセスです(ディスクスペースを節約するため)。
シリアライゼーションは、進捗状況をディスクに保存してプログラムを終了し、再びプログラムを開いた後に進捗状況をロードできることが重要なソフトウェアで特に有用です。
直列化の有用性については、ビデオゲームが最も直感的な例かもしれませんが、ユーザーの進捗状況やデータの保存と読み込みが重要なプログラムは他にもたくさんあります。
PickleとJSONの比較
JSON (JavaScript Object Notation)を聞いたことがあるかもしれません。
JSONは、開発者が文字列としてエンコードされたオブジェクトを保存、転送することもできる人気のあるフォーマットです。
このシリアライズ方法は、picklingに比べていくつかの利点があります。
JSON形式は人間が読むことができ、言語に依存せず、pickleよりも高速に処理することができます。
しかし、いくつかの重要な制限もあります。
最も重要なのは、デフォルトでは、Pythonの組み込み型の限られたサブセットしかJSONで表現できないことです。
Pickleを使えば、非常に多くのPython型、そして重要なことですが、カスタムクラスを簡単にシリアライズすることができます。
つまり、(JSONのように)カスタムスキーマを作ったり、エラーが起こりやすいシリアライザやパーサを書いたりする必要がないのです。
重い仕事はすべてPickleがやってくれます。
漬けられるもの、漬からないもの
Pickle モジュールを使ってシリアライズ、デシリアライズできるのは以下の型です。
- Python がサポートするすべてのネイティブデータ型 (boolean, None, integer, float, complex numbers, string, bytes, byte arrays)
- 辞書、セット、リスト、タプル – pickle可能なオブジェクトを含む限り。
- モジュールのトップレベルで定義された関数とクラス
pickleは言語に依存しないシリアライズ方法ではないので、pickleしたデータはPythonでないと解凍できないことを覚えておくことが重要です。
さらに、オブジェクトのピクルス化は、ピクルス化の解除に使用されるのと同じバージョンのPythonを使用していることを確認することが重要です。
この場合、Pythonのバージョンが混在すると、多くの問題が発生する可能性があります。
さらに、関数はその値ではなく、名前の参照によってピクルス化されます。
出来上がったピクルスには、関数のコードや属性の情報は含まれません。
そのため、関数のピクルスを解除する環境では、その関数をインポートできるようにしなければなりません。
つまり、関数をpickleした後、その関数が定義されていないかimportされていない環境でpickleを解除すると、例外が発生する。
また、pickleしたオブジェクトを悪用される可能性があることも非常に重要です。
例えば、信頼できないソースからのデータのピクルスを解除すると、悪意のあるコードが実行される可能性があります。
Pythonのリストのピクルス化
Python 3 の Pickle モジュールの基本的な使い方は次のとおりです。
import pickle
test_list = ['cucumber', 'pumpkin', 'carrot']
with open('test_pickle.pkl', 'wb') as pickle_out:
pickle.dump(test_list, pickle_out)
まず、1行目で pickle
モジュールをインポートしています。
3 行目では、ピクルス化する 3 要素のリストを定義しています。
5行目では、出力するPickleファイルの名前を test_pickle.pkl
としました。
wbオプションを使用することで、バイナリデータ (
b) をその中に書き込む (
w) ことをプログラムに伝えます (バイトストリームを作成したいからです)。
このチュートリアルでは、Python のドキュメントに含まれているpkl` 拡張を使用しています。
6行目では pickle.dump()
メソッドを使ってテストリストを取り出し、 test_pickle.pkl
ファイルに保存しています。
生成されたpickleファイルをテキストエディタで開いてみることをお勧めします。
バイトストリームは人間が読める形式ではないことにすぐに気がつくでしょう。
Pythonリストのアンピッキング
では、テスト用のpickleファイルの中身を解凍して、オブジェクトを元の形に戻してみましょう。
import pickle
with open('test_pickle.pkl', 'rb') as pickle_in:
unpickled_list = pickle.load(pickle_in)
print(unpickled_list)
見ての通り、この手順はオブジェクトをピクルス化したときよりも複雑ではありません。
3行目で再び test_pickle.pkl
ファイルを開きますが、今回の目的はその中に格納されているバイナリデータ (b
) を読み込むこと (r
) です。
次に、5行目で pickle.load()
メソッドを使ってリストを展開し、 unpickled_list
変数に格納しています。
そして、このリストの中身を表示して、前の例でピクルスにしたリストと同じであることを自分で確認することができます。
以下は、上記のコードを実行したときの出力です。
$ python unpickle.py
['cucumber', 'pumpkin', 'carrot']
カスタムオブジェクトのピクルスとアンピクルス
前にも書きましたが、Pickleを使うと、自分で作ったカスタムオブジェクトをシリアライズすることができます。
以下の例を見てください。
import pickle
class Veggy():
def __init__(self):
self.color = ''
def set_color(self, color):
self.color = color
cucumber = Veggy()
cucumber.set_color('green')
with open('test_pickle.pkl', 'wb') as pickle_out:
pickle.dump(cucumber, pickle_out)
with open('test_pickle.pkl', 'rb') as pickle_in:
unpickled_cucumber = pickle.load(pickle_in)
print(unpickled_cucumber.color)
見ての通り、この例も前の例と同じくらいシンプルです。
3行目から7行目にかけて、1つの属性とこの属性を変更するメソッドを含む単純なクラスを定義しています。
9行目でそのクラスのインスタンスを作成して変数 cucumber
に格納し、10行目でその属性 color
を “green” に設定します。
そして、前の例と全く同じ関数を使って、新しく作成した cucumber
オブジェクトを pickle と unpickle しています。
上のコードを実行すると、次のような出力が得られます。
$ python unpickle_custom.py
green
このオブジェクトは、クラス Veggy
が定義されているか、インポートされている環境でのみアンピクルを行うことができることに注意してください。
もし、新しいスクリプトを作成して、Veggy
クラスをインポートせずにオブジェクトをアンピックルしようとすると、”AttributeError” が発生します。
例えば、以下のスクリプトを実行してみてください。
import pickle
with open('test_pickle.pkl', 'rb') as pickle_in:
unpickled_cucumber = pickle.load(pickle_in)
print(unpickled_cucumber.color)
上のスクリプトの出力では、次のようなエラーが表示されます。
$ python unpickle_simple.py
Traceback (most recent call last):
File "<pyshell#40", line 2, in <module
unpickled_cucumber = pickle.load(pickle_in)
AttributeError: Can't get attribute 'Veggy' on <module '__main__'="" (built-in)=""
結論
ご覧の通り、PickleモジュールのおかげでPythonオブジェクトのシリアライゼーションはとてもシンプルになりました。
この例では、単純なPythonのリストをpickleしましたが、オブジェクトが他のpickle可能なオブジェクトのみを含むことを確認する限り、全く同じ方法で広範囲のPythonデータ型を保存することが可能です。
ピクルスにはいくつかの欠点がありますが、最大の欠点はPythonでしかデータのピクルスを解除できないことです – もし言語横断的なソリューションが必要なら、JSONの方が間違いなく良い選択肢です。
そして最後に、ピクルスは必ずしも実行したくないコードを運ぶために使うことができることを覚えておいてください。
漬物と同じように、信頼できるソースから漬物を入手する限り、大丈夫でしょう。
#40> です。