11.1. pickle
— Python オブジェクトの直列化¶
pickle
モジュールでは、Python オブジェクトデータ構造を直列化 (serialize) したり非直列化 (de-serialize)するための基礎的ですが強力なアルゴリズムを実装しています。 "Pickle 化 (Pickling)" は Python のオブジェクト階層をバイトストリームに変換する過程を指します。"非 Pickle 化 (unpickling)" はその逆の操作で、バイトストリームをオブジェクト階層に戻すように変換します。Pickle 化 (及び非 Pickle 化) は、別名 "直列化 (serialization)" や "整列化 (marshalling)" [1] 、 "平坦化 (flattening)" として知られていますが、ここでは混乱を避けるため、用語として "Pickle 化" および "非Pickle 化" を使います。
このドキュメントでは pickle
モジュールおよび cPickle
モジュールの両方について記述します。
警告
pickle
モジュールはエラーや不正に生成されたデータに対して安全ではありません。信頼できない、あるいは認証されていないソースから受け取ったしたデータを unpickle してはいけません。
11.1.1. 他の Python モジュールとの関係¶
pickle
モジュールには cPickle
と呼ばれる最適化のなされた親類モジュールがあります。名前が示すように、 cPickle
は C で書かれており、このため pickle
より 1000 倍くらいまで高速になる可能性があります。しかしながら cPickle
では Pickler()
および Unpickler()
クラスのサブクラス化をサポートしていません。これは cPickle
では、これらは関数であってクラスではないからです。ほとんどのアプリケーションではこの機能は不要であり、 cPickle
の持つ高いパフォーマンスの恩恵を受けることができます。その他の点では、二つのモジュールにおけるインタフェースはほとんど同じです; このマニュアルでは共通のインタフェースを記述しており、必要に応じてモジュール間の相違について指摘します。以下の議論では、 pickle
と cPickle
の総称として "pickle" という用語を使うことにします。
これら二つのモジュールが生成するデータストリームは相互交換できることが保証されています。
Python には marshal
と呼ばれるより原始的な直列化モジュールがありますが、一般的に Python オブジェクトを直列化する方法としては pickle
を選ぶべきです。 marshal
は基本的に .pyc
ファイルをサポートするために存在しています。
pickle
モジュールはいくつかの点で marshal
と明確に異なります:
pickle
モジュールでは、同じオブジェクトが再度直列化されることのないよう、すでに直列化されたオブジェクトについて追跡情報を保持します。marshal
はこれを行いません。この機能は再帰的オブジェクトと共有オブジェクトの両方に重要な関わりをもっています。再帰的オブジェクトとは自分自身に対する参照を持っているオブジェクトです。再帰的オブジェクトは marshal で扱うことができず、実際、再帰的オブジェクトを marshal 化しようとすると Python インタプリタをクラッシュさせてしまいます。共有オブジェクトは、直列化しようとするオブジェクト階層の異なる複数の場所で同じオブジェクトに対する参照が存在する場合に生じます。共有オブジェクトを共有のままにしておくことは、変更可能なオブジェクトの場合には非常に重要です。
marshal
はユーザ定義クラスやそのインスタンスを直列化するために使うことができません。pickle
はクラスインスタンスを透過的に保存したり復元したりすることができますが、クラス定義をインポートすることが可能で、かつオブジェクトが保存された際と同じモジュールで定義されていなければなりません。marshal
の直列化フォーマットは Python の異なるバージョンで可搬性があることを保証していません。marshal
の本来の仕事は.pyc
ファイルのサポートなので、Python を実装する人々には、必要に応じて直列化フォーマットを以前のバージョンと互換性のないものに変更する権限が残されています。pickle
直列化フォーマットには、全ての Python リリース間で以前のバージョンとの互換性が保証されています。
直列化は永続化 (persisitence) よりも原始的な概念です; pickle
はファイルオブジェクトを読み書きしますが、永続化されたオブジェクトの名前付け問題や、(より複雑な) オブジェクトに対する競合アクセスの問題を扱いません。 pickle
モジュールは複雑なオブジェクトをバイトストリームに変換することができ、バイトストリームを変換前と同じ内部構造をオブジェクトに変換することができます。このバイトストリームの最も明白な用途はファイルへの書き込みですが、その他にもネットワークを介して送信したり、データベースに記録したりすることができます。モジュール shelve
はオブジェクトを DBM 形式のデータベースファイル上で pickle 化したり unpickle 化したりするための単純なインタフェースを提供しています。
11.1.2. データストリームの形式¶
pickle
が使うデータ形式は Python 特有です。そうすることで、XDR のような外部の標準が持つ制限 (例えば XDR ではポインタの共有を表現できません) を課せられることがないという利点があります; しかしこれは Python で書かれていないプログラムが pickle 化された Python オブジェクトを再構築できない可能性があることを意味します。
標準では、 pickle
データ形式では印字可能な ASCII 表現を使います。これはバイナリ表現よりも少しかさばるデータになります。印字可能な ASCII の利用 (とその他の pickle
表現形式が持つ特徴) の大きな利点は、デバッグやリカバリを目的とした場合に、 pickle 化されたファイルを標準的なテキストエディタで読めるということです。
現在、pickle化に使われるプロトコルは、以下の 3 種類です。
- バージョン 0 のプロトコルは、最初の ASCII プロトコルで、以前のバージョンのPython と後方互換です。
- バージョン 1 のプロトコルは、古いバイナリ形式で、以前のバージョンの Python と後方互換です。
- バージョン 2 のプロトコルは、Python 2.3 で導入されました。 new-style class を、より効率よく piclke 化します。
詳細は PEP 307 を参照してください。
protocol を指定しない場合、プロトコル 0 が使われます。 protocol に負値か HIGHEST_PROTOCOL
を指定すると、有効なプロトコルの内、もっとも高いバージョンのものが使われます。
バージョン 2.3 で変更: protocol パラメータが導入されました。
protocol version >= 1 を指定することで、少しだけ効率の高いバイナリ形式を選ぶことができます。
11.1.3. 使用法¶
オブジェクト階層を直列化するには、まず pickler を生成し、続いてpickler の dump()
メソッドを呼び出します。データストリームから非直列化するには、まず unpickler を生成し、続いて unpicklerの load()
メソッドを呼び出します。 pickle
モジュールでは以下の定数を提供しています:
-
pickle.
HIGHEST_PROTOCOL
¶ 有効なプロトコルのうち、最も大きいバージョン。この値は、 protocol として渡せます。
バージョン 2.3 で追加.
注釈
protocols >= 1 で作られた pickle ファイルは、常にバイナリモードでオープンするようにしてください。古い ASCII ベースの pickle プロトコル 0 では、矛盾しない限りにおいてテキストモードとバイナリモードのいずれも利用することができます。
プロトコル 0 で書かれたバイナリの pickle ファイルは、行ターミネータとして単独の改行(LF)を含んでいて、ですのでこの形式をサポートしない、 Notepad や他のエディタで見たときに「おかしく」見えるかもしれません。
この pickle 化の手続きを便利にするために、 pickle
モジュールでは以下の関数を提供しています:
-
pickle.
dump
(obj, file[, protocol])¶ すでに開かれているファイルオブジェクト file に、 obj を pickle 化したものを表現する文字列を書き込みます。
Pickler(file, protocol).dump(obj)
と同じです。protocol を指定しない場合、プロトコル 0 が使われます。 protocol に負値か
HIGHEST_PROTOCOL
を指定すると、有効なプロトコルの内、もっとも高いバージョンのものが使われます。バージョン 2.3 で変更: protocol パラメータが導入されました。
file は、単一の文字列引数を受理する
write()
メソッドを持たなければなりません。従って、 file としては、書き込みのために開かれたファイルオブジェクト、StringIO
オブジェクト、その他前述のインタフェースに適合する他のカスタムオブジェクトをとることができます。
-
pickle.
load
(file)¶ すでに開かれているファイルオブジェクト file から文字列を読み出し、読み出された文字列を pickle 化されたデータ列として解釈して、もとのオブジェクト階層を再構築して返します。
Unpickler(file).load()
と同じです。file は、整数引数をとる
read()
メソッドと、引数の必要ないreadline()
メソッドを持たなければなりません。これらのメソッドは両方とも文字列を返さなければなりません。従って、 file としては、読み出しのために開かれたファイルオブジェクト、StringIO
オブジェクト、その他前述のインタフェースに適合する他のカスタムオブジェクトをとることができます。この関数はデータ列の書き込まれているモードがバイナリかそうでないかを自動的に判断します。
-
pickle.
dumps
(obj[, protocol])¶ obj の pickle 化された表現を、ファイルに書き込む代わりに文字列で返します。
protocol を指定しない場合、プロトコル 0 が使われます。 protocol に負値か
HIGHEST_PROTOCOL
を指定すると、有効なプロトコルの内、もっとも高いバージョンのものが使われます。バージョン 2.3 で変更: protocol パラメータが追加されました。
-
pickle.
loads
(string)¶ pickle 化されたオブジェクト階層を文字列から読み出します。文字列中で pickle 化されたオブジェクト表現よりも後に続く文字列は無視されます。
pickle
モジュールでは、以下の 3 つの例外も定義しています:
-
exception
pickle.
UnpicklingError
¶ この例外は、オブジェクトを unpickle 化する際に問題が発生した場合に送出されます。 unpickle 化中には
AttributeError
、EOFError
、ImportError
、およびIndexError
といった他の例外 (これだけとは限りません) も発生する可能性があるので注意してください。
pickle
モジュールでは、2 つの呼び出し可能オブジェクト [2] として、 Pickler
および Unpickler
を提供しています:
-
class
pickle.
Pickler
(file[, protocol])¶ pickle 化されたオブジェクトのデータ列を書き込むためのファイル類似のオブジェクトを引数にとります。
protocol を指定しない場合、プロトコル 0 が使われます。 protocol に負値か
HIGHEST_PROTOCOL
を指定すると、有効なプロトコルの内、もっとも高いバージョンのものが使われます。バージョン 2.3 で変更: protocol パラメータが導入されました。
file は単一の文字列引数を受理する
write()
メソッドを持たなければなりません。従って、 file としては、書き込みのために開かれたファイルオブジェクト、StringIO
オブジェクト、その他前述のインタフェースに適合する他のカスタムオブジェクトをとることができます。Pickler
オブジェクトでは、一つ (または二つ) の public なメソッドを定義しています:-
dump
(obj)¶ コンストラクタで与えられた、すでに開かれているファイルオブジェクトに obj の pickle 化された表現を書き込みます。コンストラクタに渡された protocol 引数の値に応じて、バイナリおよびASCII 形式が使われます。
-
clear_memo
()¶ picller の "メモ" を消去します。メモとは、共有オブジェクトまたは再帰的なオブジェクトが値ではなく参照で記憶されるようにするために、 pickler がこれまでどのオブジェクトに遭遇してきたかを記憶するデータ構造です。このメソッドは pickler を再利用する際に便利です。
注釈
Python 2.3 以前では、
clear_memo()
はcPickle
で生成された pickler でのみ利用可能でした。pickle
モジュールでは、pickler はmemo
と呼ばれる Python 辞書型のインスタンス変数を持ちます。従って、pickler
モジュールにおける pickler のメモを消去は、以下のようにしてできます:mypickler.memo.clear()
以前のバージョンの Python での動作をサポートする必要のないコードでは、単に
clear_memo()
を使ってください。
-
同じ Pickler
のインスタンスに対し、 dump()
メソッドを複数回呼び出すことは可能です。この呼び出しは、対応する Unpickler
インスタンスで同じ回数だけ load()
を呼び出す操作に対応します。同じオブジェクトが dump()
を複数回呼び出して pickle 化された場合、 load()
は全て同じオブジェクトに対して参照を行います [3] 。
Unpickler
オブジェクトは以下のように定義されています:
-
class
pickle.
Unpickler
(file)¶ pickle データ列を読み出すためのファイル類似のオブジェクトを引数に取ります。このクラスはデータ列がバイナリモードかどうかを自動的に判別します。従って、
Pickler
のファクトリメソッドのようなフラグを必要としません。file は、整数引数をとる
read()
メソッドと、引数の必要ないreadline()
メソッドを持たなければなりません。これらのメソッドは両方とも文字列を返さなければなりません。従って、 file としては、読み出しのために開かれたファイルオブジェクト、StringIO
オブジェクト、その他前述のインタフェースに適合する他のカスタムオブジェクトをとることができます。Unpickler
オブジェクトは 1 つ (または 2 つ) の public なメソッドを持っています:-
load
()¶ コンストラクタで渡されたファイルオブジェクトからオブジェクトの pickle 化表現を読み出し、中に収められている再構築されたオブジェクト階層を返します。
このメソッドは自動的にデータストリームがバイナリモードで書き出されているかどうかを判別します。
-
11.1.4. 何を pickle 化したり unpickle 化できるのか?¶
以下の型は pickle 化できます:
None
、True
、およびFalse
- 整数、長整数、浮動小数点数、複素数
- 通常文字列および Unicode 文字列
- pickle 化可能なオブジェクトからなるタプル、リスト、集合および辞書
- モジュールのトップレベルで定義されている関数
- モジュールのトップレベルで定義されている組込み関数
- モジュールのトップレベルで定義されているクラス
__dict__
属性を持つクラス、あるいは__getstate__()
メソッドの返り値が pickle 化可能なクラス (詳細は pickle 化プロトコル を参照)。
pickle 化できないオブジェクトを pickle 化しようとすると、 PicklingError
例外が送出されます; この例外が起きた場合、背後のファイルには未知の長さのバイト列が書き込まれてしまいます。極端に再帰的なデータ構造を pickle 化しようとした場合には再帰の深さ制限を越えてしまうかもしれず、この場合には RuntimeError
が送出されます。この制限は、 sys.setrecursionlimit()
で慎重に上げていくことは可能です。
(組み込みおよびユーザ定義の) 関数は、値ではなく "完全記述された" 参照名として pickle 化されるので注意してください。これは、関数の定義されているモジュールの名前と一緒と併せ、関数名だけが pickle 化されることを意味します。関数のコードや関数の属性は何も pickle化されません。従って、定義しているモジュールは unpickle 化環境で import 可能でなければならず、そのモジュールには指定されたオブジェクトが含まれていなければなりません。そうでない場合、例外が送出されます [4] 。
クラスも同様に名前参照で pickle 化されるので、unpickle 化環境には同じ制限が課せられます。クラス中のコードやデータは何も pickle 化されないので、以下の例ではクラス属性 attr
が unpickle 化環境で復元されないことに注意してください
class Foo:
attr = 'a class attr'
picklestring = pickle.dumps(Foo)
pickle 化可能な関数やクラスがモジュールのトップレベルで定義されていなければならないのはこれらの制限のためです。
同様に、クラスのインスタンスが pickle 化された際、そのクラスのコードおよびデータはオブジェクトと一緒に pickle 化されることはありません。インスタンスのデータのみが pickle 化されます。この仕様は、クラス内のバグを修正したりメソッドを追加した後でも、そのクラスの以前のバージョンで作られたオブジェクトを読み出せるように意図的に行われています。あるクラスの多くのバージョンで使われるような長命なオブジェクトを作ろうと計画しているなら、そのクラスの __setstate__()
メソッドによって適切な変換が行われるようにオブジェクトのバージョン番号を入れておくとよいかもしれません。
11.1.5. pickle 化プロトコル¶
この節では pickler/unpickler と直列化対象のオブジェクトとの間のインタフェースを定義する "pickle 化プロトコル"について記述します。このプロトコルは自分のオブジェクトがどのように直列化されたり非直列化されたりするかを定義し、カスタマイズし、制御するための標準的な方法を提供します。この節での記述は、unpickle 化環境を不信な pickle 化データに対して安全にするために使う特殊なカスタマイズ化についてはカバーしていません; 詳細は Unpickler をサブクラス化する を参照してください。
11.1.5.1. 通常のクラスインスタンスの pickle 化および unpickle 化¶
-
object.
__getinitargs__
()¶ pickle 化されたクラスインスタンスが unpickle 化されたとき、
__init__()
メソッドは通常呼び出され ません 。 unpickle 化の際に__init__()
が呼び出される方が望ましい場合、旧スタイルクラスではメソッド__getinitargs__()
を定義することができます。このメソッドはクラスコンストラクタ (例えば__init__()
) に渡されるべき位置引数からなる タプル を返さなければなりません。__getinitargs__()
メソッドは pickle 時に呼び出されます; この関数が返すタプルはインスタンスの pickle 化データに組み込まれます。
-
object.
__getnewargs__
()¶ 新スタイルクラスでは、プロトコル 2 で呼び出される
__getnewargs__()
を定義する事ができます。インスタンス生成時に内部的な不変条件が成立する必要があったり、(タプルや文字列のように)型の__new__()
メソッドに指定する引数によってメモリの割り当てを変更する必要がある場合には__getnewargs__()
を定義してください。新スタイルクラスC
のインスタンスは、次のように生成されます。:obj = C.__new__(C, *args)
ここで args は元のオブジェクトの
__getnewargs__()
メソッドを呼び出した時の戻り値となります。__getnewargs__()
を定義していない場合、 args は空のタプルとなります。
-
object.
__getstate__
()¶ クラスは、インスタンスの pickle 化方法にさらに影響を与えることができます; クラスが
__getstate__()
メソッドを定義している場合、このメソッドが呼び出され、返された状態値はインスタンスの内容として、インスタンスの辞書の代わりに pickle 化されます。__getstate__()
メソッドが定義されていない場合、インスタンスの__dict__
の内容が pickle 化されます。
-
object.
__setstate__
(state)¶ unpickle 化では、クラスが
__setstate__()
も定義していた場合、 unpickle 化された状態値とともに呼び出されます。 [5]__setstate__()
メソッドが定義されていない場合、pickle 化された状態は辞書型でなければならず、その要素は新たなインスタンスの辞書に代入されます。クラスが__getstate__()
と__setstate__()
の両方を定義している場合、状態値オブジェクトは辞書である必要はなく、これらのメソッドは期待通りの動作を行います。 [6]注釈
新しいスタイルのクラスにおいて
__getstate__()
が偽値を返す場合、__setstate__()
メソッドは呼ばれません。
注釈
unpickleするとき、 __getattr__()
, __getattribute__()
, __setattr__()
といったメソッドがインスタンスに対して呼ばれます。これらのメソッドが何か内部の不変条件に依存しているのであれば、その型は __getinitargs__()
か __getnewargs__()
のどちらかを実装してその不変条件を満たせるようにするべきです。それ以外の場合、 __new__()
も __init__()
も呼ばれません。
11.1.5.2. 拡張型の pickle 化および unpickle 化¶
-
object.
__reduce__
()¶ Pickler
が全く未知の型の — 拡張型のような — オブジェクトに遭遇した場合、pickle 化方法のヒントとして 2 個所を探します。第一は__reduce__()
メソッドを実装しているかどうかです。もし実装されていれば、pickle 化時に__reduce__()
メソッドが引数なしで呼び出されます。メソッドはこの呼び出しに対して文字列またはタプルのどちらかを返さねばなりません。文字列を返す場合、その文字列は通常通りに pickle 化されるグローバル変数の名前を指しています。
__reduce__()
の返す文字列は、モジュールにからみてオブジェクトのローカルな名前でなければなりません; pickle モジュールはモジュールの名前空間を検索して、オブジェクトの属するモジュールを決定します。タプルを返す場合、タプルの要素数は 2 から 5 でなければなりません。オプションの要素は省略したり
None
を指定したりできます。各要素の意味づけは以下の通りです:オブジェクトの初期バージョンを生成するために呼び出される呼び出し可能オブジェクトです。この呼び出し可能オブジェクトへの引数はタプルの次の要素で与えられます。それ以降の要素では pickle 化されたデータを完全に再構築するために使われる付加的な状態情報が与えられます。
逆 pickle 化の環境下では、このオブジェクトはクラスか、 "安全なコンストラクタ (safe constructor, 下記参照)" として登録されていたり属性
__safe_for_unpickling__
の値が真であるような呼び出し可能オブジェクトでなければなりません。そうでない場合、逆 pickle 化を行う環境でUnpicklingError
が送出されます。通常通り、 callable は名前だけで pickle 化されるので注意してください。呼び出し可能なオブジェクトのための引数からなるタプル
バージョン 2.5 で変更: 以前は、この引数には
None
もあり得ました。オプションとして、 通常のクラスインスタンスの pickle 化および unpickle 化 節で記述されているようにオブジェクトの
__setstate__()
メソッドに渡される、オブジェクトの状態。オブジェクトが__setstate__()
メソッドを持たない場合、上記のように、この値は辞書でなくてはならず、オブジェクトの__dict__
に追加されます。オプションとして、リスト中の連続する要素を返すイテレータ (シーケンスではありません)。このリストの要素は pickle 化され、
obj.append(item)
またはobj.extend(list_of_items)
のいずれかを使って追加されます。主にリストのサブクラスで用いられていますが、他のクラスでも、適切なシグネチャのappend()
やextend()
を備えている限り利用できます。 (append()
とextend()
のいずれを使うかは、どのバージョンの pickle プロトコルを使っているか、そして追加する要素の数で決まります。従って両方のメソッドをサポートしていなければなりません。)オプションとして、辞書中の連続する要素を返すイテレータ (シーケンスではありません)。このリストの要素は
(key, value)
という形式でなければなりません。要素は pickle 化され、obj[key] = value
を使ってオブジェクトに格納されます。主に辞書のサブクラスで用いられていますが、他のクラスでも、__setitem__()
を備えている限り利用できます。
-
object.
__reduce_ex__
(protocol)¶ __reduce__()
を実装する場合、プロトコルのバージョンを知っておくと便利なことがあります。これは__reduce__()
の代わりに__reduce_ex__()
を使って実現できます。__reduce_ex__()
が定義されている場合、__reduce__()
よりも優先して呼び出されます (以前のバージョンとの互換性のために__reduce__()
を残しておいてもかまいません)。__reduce_ex__()
はプロトコルのバージョンを表す整数の引数を一つ伴って呼び出されます。object
クラスでは__reduce__()
と__reduce_ex__()
の両方を定義しています。とはいえ、サブクラスで__reduce__()
をオーバライドしており、__reduce_ex__()
をオーバライドしていない場合には、__reduce_ex__()
の実装がそれを検出して__reduce__()
を呼び出すようになっています。
pickle 化するオブジェクト上で __reduce__()
メソッドを実装する代わりに、 copy_reg
モジュールを使って呼び出し可能オブジェクトを登録する方法もあります。このモジュールはプログラムに "縮小化関数 (reduction function)" とユーザ定義型のためのコンストラクタを登録する方法を提供します。縮小化関数は、単一の引数として pickle 化するオブジェクトをとることを除き、上で述べた __reduce__()
メソッドと同じ意味とインタフェースを持ちます。
登録されたコンストラクタは上で述べたような unpickle 化については "安全なコンストラクタ" であると考えられます。
11.1.5.3. 外部オブジェクトの pickle 化および unpickle 化¶
オブジェクトの永続化を便利にするために、 pickle
は pickle 化されたデータ列上にないオブジェクトに対して参照を行うという概念をサポートしています。これらのオブジェクトは "永続化 id (persistent id)" で参照されており、この id は単に印字可能なASCII 文字からなる任意の文字列です。これらの名前の解決方法は pickle
モジュールでは定義されていません; オブジェクトはこの名前解決を pickler および unpickler 上のユーザ定義関数にゆだねます [7] 。
外部永続化 id の解決を定義するには、pickler オブジェクトの persistent_id
属性と、 unpickler オブジェクトの persistent_load
属性を設定する必要があります。
外部永続化 id を持つオブジェクトを pickle 化するには、pickler は自作の persistent_id()
メソッドを持たなければなりません。このメソッドは一つの引数をとり、 None
とオブジェクトの永続化 id のうちどちらかを返さなければなりません。 None
が返された場合、 pickler は単にオブジェクトを通常のように pickle 化するだけです。永続化 id 文字列が返された場合、 piclkler はその文字列に対して、unpickler がこの文字列を永続化 id として認識できるように、マーカと共に pickle 化します。
外部オブジェクトを unpickle 化するには、unpickler は自作の persistent_load()
関数を持たなければなりません。この関数は永続化 id 文字列を引数にとり、参照されているオブジェクトを返します。
多分 より理解できるようになるようなちょっとした例を以下に示します:
import pickle
from cStringIO import StringIO
src = StringIO()
p = pickle.Pickler(src)
def persistent_id(obj):
if hasattr(obj, 'x'):
return 'the value %d' % obj.x
else:
return None
p.persistent_id = persistent_id
class Integer:
def __init__(self, x):
self.x = x
def __str__(self):
return 'My name is integer %d' % self.x
i = Integer(7)
print i
p.dump(i)
datastream = src.getvalue()
print repr(datastream)
dst = StringIO(datastream)
up = pickle.Unpickler(dst)
class FancyInteger(Integer):
def __str__(self):
return 'I am the integer %d' % self.x
def persistent_load(persid):
if persid.startswith('the value '):
value = int(persid.split()[2])
return FancyInteger(value)
else:
raise pickle.UnpicklingError, 'Invalid persistent id'
up.persistent_load = persistent_load
j = up.load()
print j
cPickle
モジュール内では、 unpickler の persistent_load
属性は Pythonリスト型として設定することができます。この場合、 unpickler が永続化 id に遭遇しても、永続化 id 文字列は単にリストに追加されるだけです。この仕様は、pickle データ中の全てのオブジェクトを実際にインスタンス化しなくても、 pickle データ列中でオブジェクトに対する参照を "嗅ぎ回る" ことができるようにするために存在しています [8] 。リストに persistent_load
を設定するやり方は、よく Unpickler クラスの noload()
メソッドと共に使われます。
11.1.6. Unpickler をサブクラス化する¶
デフォルトでは、逆 pickle 化は pickle 化されたデータ中に見つかったクラスを import することになります。自前の unpickler をカスタマイズすることで、何が unpickle 化されて、どのメソッドが呼び出されるかを厳密に制御することはできます。しかし不運なことに、厳密になにを行うべきかは pickle
と cPickle
のどちらを使うかで異なります [9] 。
pickle
モジュールでは、 Unpickler
からサブクラスを派生し、 load_global()
メソッドを上書きする必要があります。 load_global()
は pickle データ列から最初の 2 行を読まなければならず、ここで最初の行はそのクラスを含むモジュールの名前、2 行目はそのインスタンスのクラス名になるはずです。次にこのメソッドは、例えばモジュールをインポートして属性を掘り起こすなどしてクラスを探し、発見されたものを unpickler のスタックに置きます。その後、このクラスは空のクラスの __class__
属性に代入する方法で、クラスの __init__()
を使わずにインスタンスを魔法のように生成します。あなたの作業は (もしその作業を受け入れるなら)、unpickler のスタックの上に push された load_global()
を、unpickle しても安全だと考えられる何らかのクラスの既知の安全なバージョンにすることです。あるいは全てのインスタンスに対して unpickling を許可したくないならエラーを送出してください。このからくりがハックのように思えるなら、あなたは間違っていません。このからくりを動かすには、ソースコードを参照してください。
cPickle
では事情は多少すっきりしていますが、十分というわけではありません。何を unpickle 化するかを制御するには、 unpickler の find_global
属性を関数か None
に設定します。属性が None
の場合、インスタンスを unpickle しようとする試みは全て UnpicklingError
を送出します。属性が関数の場合、この関数はモジュール名またはクラス名を受理し、対応するクラスオブジェクトを返さなくてはなりません。このクラスが行わなくてはならないのは、クラスの探索、必要な import のやり直しです。そしてそのクラスのインスタンスが unpickle 化されるのを防ぐためにエラーを送出することもできます。
以上の話から言えることは、アプリケーションが unpickle 化する文字列の発信元については非常に高い注意をはらわなくてはならないということです。
11.1.7. 例¶
いちばん単純には、 dump()
と load()
を使用してください。自己参照リストが正しく pickle 化およびリストアされることに注目してください。
import pickle
data1 = {'a': [1, 2.0, 3, 4+6j],
'b': ('string', u'Unicode string'),
'c': None}
selfref_list = [1, 2, 3]
selfref_list.append(selfref_list)
output = open('data.pkl', 'wb')
# Pickle dictionary using protocol 0.
pickle.dump(data1, output)
# Pickle the list using the highest protocol available.
pickle.dump(selfref_list, output, -1)
output.close()
以下の例は pickle 化された結果のデータを読み込みます。 pickle を含むデータを読み込む場合、ファイルはバイナリモードでオープンしなければいけません。これは ASCII 形式とバイナリ形式のどちらが使われているかは分からないからです。
import pprint, pickle
pkl_file = open('data.pkl', 'rb')
data1 = pickle.load(pkl_file)
pprint.pprint(data1)
data2 = pickle.load(pkl_file)
pprint.pprint(data2)
pkl_file.close()
より大きな例で、クラスを pickle 化する挙動を変更するやり方を示します。 TextReader
クラスはテキストファイルを開き、 readline()
メソッドが呼ばれるたびに行番号と行の内容を返します。 TextReader
インスタンスが pickle 化された場合、ファイルオブジェクト 以外の 全ての属性が保存されます。インスタンスが unpickle 化された際、ファイルは再度開かれ、以前のファイル位置から読み出しを再開します。上記の動作を実装するために、 __setstate__()
および __getstate__()
メソッドが使われています。
#!/usr/local/bin/python
class TextReader:
"""Print and number lines in a text file."""
def __init__(self, file):
self.file = file
self.fh = open(file)
self.lineno = 0
def readline(self):
self.lineno = self.lineno + 1
line = self.fh.readline()
if not line:
return None
if line.endswith("\n"):
line = line[:-1]
return "%d: %s" % (self.lineno, line)
def __getstate__(self):
odict = self.__dict__.copy() # copy the dict since we change it
del odict['fh'] # remove filehandle entry
return odict
def __setstate__(self, dict):
fh = open(dict['file']) # reopen file
count = dict['lineno'] # read from file...
while count: # until line count is restored
fh.readline()
count = count - 1
self.__dict__.update(dict) # update attributes
self.fh = fh # save the file object
使用例は以下のようになるでしょう:
>>> import TextReader
>>> obj = TextReader.TextReader("TextReader.py")
>>> obj.readline()
'1: #!/usr/local/bin/python'
>>> obj.readline()
'2: '
>>> obj.readline()
'3: class TextReader:'
>>> import pickle
>>> pickle.dump(obj, open('save.p', 'wb'))
pickle
が Python プロセス間でうまく働くことを見たいなら、先に進む前に他の Python セッションを開始してください。以下の振る舞いは同じプロセスでも新たなプロセスでも起こります。
>>> import pickle
>>> reader = pickle.load(open('save.p', 'rb'))
>>> reader.readline()
'4: """Print and number lines in a text file."""'
11.2. cPickle
— より高速な pickle
¶
cPickle
モジュールは Python オブジェクトの直列化および非直列化をサポートし、 pickle
モジュールとほとんど同じインタフェースと機能を提供します。いくつか相違点がありますが、最も重要な違いはパフォーマンスとサブクラス化が可能かどうかです。
第一に、 cPickle
は C で実装されているため、 pickle
よりも最大で 1000 倍高速です。第二に、 cPickle
モジュール内では、呼び出し可能オブジェクト Pickler()
および Unpickler()
は関数で、クラスではありません。つまり、pickle 化や unpickle 化を行うカスタムのサブクラスを派生することができないということです。多くのアプリケーションではこの機能は不要なので、 cPickle
モジュールによる大きなパフォーマンス向上の恩恵を受けられるはずです。
pickle
と cPickle
で作られた pickle データ列は同じなので、既存の pickle データに対して pickle
と cPickle
を互換に使用することができます。 [10]
cPickle
と pickle
の API 間には他にも些細な相違がありますが、ほとんどのアプリケーションで互換性があります。より詳細なドキュメンテーションは pickle
のドキュメントにあり、そこでドキュメント化されている相違点について挙げています。
脚注
[1] | marshal モジュールと間違えないように注意してください。 |
[2] | pickle では、これらの呼び出し可能オブジェクトはクラスであり、サブクラス化してその動作をカスタマイズすることができます。しかし、 cPickle モジュールでは、これらの呼び出し可能オブジェクトはファクトリ関数であり、サブクラス化することができません。サブクラスを作成する共通の理由の一つは、どのオブジェクトを実際に unpickle するかを制御することです。詳細については Unpickler をサブクラス化する を参照してください。 |
[3] | 警告: これは、複数のオブジェクトを pickle 化する際に、オブジェクトやそれらの一部に対する変更を妨げないようにするための仕様です。あるオブジェクトに変更を加えて、その後同じ Pickler を使って再度 pickle 化しようとしても、そのオブジェクトは pickle 化しなおされません — そのオブジェクトに対する参照が pickle 化され、 Unpickler は変更された値ではなく、元の値を返します。これには 2 つの問題点 : (1) 変更の検出、そして (2) 最小限の変更を整列化すること、があります。ガーベジコレクションもまた問題になります。 |
[4] | 送出される例外は ImportError や AttributeError になるはずですが、他の例外も起こりえます。 |
[5] | これらのメソッドはクラスインスタンスのコピーを実装する際にも用いられます。 |
[6] | このプロトコルはまた、 copy で定義されている浅いコピーや深いコピー操作でも用いられます。 |
[7] | ユーザ定義関数に関連付けを行うための実際のメカニズムは、 pickle および cPickle では少し異なります。 pickle のユーザは、サブクラス化を行い、 persistend_id() および persistent_load() メソッドを上書きすることで同じ効果を得ることができます。 |
[8] | Guide と Jim が居間に座り込んでピクルス (pickles) を嗅いでいる光景を想像してください。 |
[9] | 注意してください: ここで記述されている機構は内部の属性とメソッドを使っており、これらはPython の将来のバージョンで変更される対象になっています。われわれは将来、この挙動を制御するための、 pickle および cPickle の両方で動作する、共通のインタフェースを提供するつもりです。 |
[10] | pickle データ形式は実際には小規模なスタック指向のプログラム言語であり、またあるオブジェクトをエンコードする際に多少の自由度があるため、二つのモジュールが同じ入力オブジェクトに対して異なるデータ列を生成することもあります。しかし、常に互いに他のデータ列を読み出せることが保証されています。 |