pickle
--- Python オブジェクトの直列化¶
ソースコード: Lib/pickle.py
pickle
モジュールは Python オブジェクトの直列化および直列化されたオブジェクトの復元のためのバイナリプロトコルを実装しています。"Pickle 化" は Python オブジェクト階層をバイトストリームに変換する処理、"非 pickle 化" は (バイナリファイル または バイトライクオブジェクト から) バイトストリームをオブジェクト階層に復元する処理を意味します。pickle 化 (および非 pickle 化) は "直列化 (serialization)"、"整列化 (marshalling)"、あるいは 1 "平坦化 (flattening)" とも呼ばれますが、混乱を避けるため、ここでは "Pickle 化"、"非 pickle 化" で統一します。
警告
``pickle``モジュールは 安全ではありません 。信頼できるデータのみを非 pickle 化してください。
非 pickle 化の過程で任意のコードを実行する ような、悪意ある pickle オブジェクトを生成することが可能です。信頼できない提供元からのデータや、改竄された可能性のあるデータの非 pickle 化は絶対に行わないでください。
データが改竄されていないことを保証したい場合は、 hmac
による鍵付きハッシュ化を検討してください。
信頼できないデータを処理する場合 json
のようなより安全な直列化形式の方が適切でしょう。 json との比較 を参照してください。
他の Python モジュールとの関係¶
marshal
との比較¶
Python には marshal
と呼ばれるより原始的な直列化モジュールがありますが、一般的に Python オブジェクトを直列化する方法としては pickle
を選ぶべきです。 marshal
は基本的に .pyc
ファイルをサポートするために存在しています。
pickle
モジュールはいくつかの点で marshal
と明確に異なります:
pickle
モジュールでは、同じオブジェクトが再度直列化されることのないよう、すでに直列化されたオブジェクトについて追跡情報を保持します。marshal
はこれを行いません。この機能は再帰的オブジェクトと共有オブジェクトの両方に重要な関わりをもっています。再帰的オブジェクトとは自分自身に対する参照を持っているオブジェクトです。再帰的オブジェクトは marshal で扱うことができず、実際、再帰的オブジェクトを marshal 化しようとすると Python インタプリタをクラッシュさせてしまいます。共有オブジェクトは、直列化しようとするオブジェクト階層の異なる複数の場所で同じオブジェクトに対する参照が存在する場合に生じます。共有オブジェクトを共有のままにしておくことは、変更可能なオブジェクトの場合には非常に重要です。
marshal
はユーザ定義クラスやそのインスタンスを直列化するために使うことができません。pickle
はクラスインスタンスを透過的に保存したり復元したりすることができますが、クラス定義をインポートすることが可能で、かつオブジェクトが保存された際と同じモジュールで定義されていなければなりません。marshal
の直列化形式は Python のバージョン間での移植性を保証していません。.pyc
ファイルをサポートすることが主な役割であるため、 Python 開発者は必要があれば直列化形式に非互換な変更を加える権利を有しています。 いっぽうpickle
の直列化形式は、互換性のあるプロトコルを選ぶという条件のもとで Python リリース間の後方互換性が保証されます。また処理すべきデータが Python 2 と Python 3 の間で非互換な型を含む場合も、 pickle 化および非 pickle 化のコードはそのような互換性を破る言語の境界を適切に取り扱います。
json
との比較¶
pickle プロトコルと JSON (JavaScript Object Notation) との基本的な違いは以下のとおりです:
JSON はテキストの直列化フォーマット (大抵の場合
utf-8
にエンコードされますが、その出力は Unicode 文字列です) で、pickle はバイナリの直列化フォーマットです;JSON は人間が読める形式ですが、pickle はそうではありません;
JSON は相互運用可能で Python 以外でも広く使用されていますが、pickle は Python 固有です;
JSON は、デフォルトでは Python の組み込み型の一部しか表現することができず、カスタムクラスに対しても行えません; pickle は極めて多くの Python 組み込み型を表現できます (その多くは賢い Python 内省機構によって自動的に行われます; 複雑なケースでは 固有のオブジェクト API によって対応できます)。
pickleとは異なり、信頼できないJSONを復元するだけでは、任意のコードを実行できる脆弱性は発生しません。
参考
json
モジュール: JSON への直列化および復元を行うための標準ライブラリモジュール。
データストリームの形式¶
pickle
によって使用されるデータフォーマットは Python 固有です。これは、JSON や XDR のような外部標準によって (例えばポインター共有を表わすことができないといったような) 制限を受けることがないという利点があります; ただし、これは非 Python プログラムが pickle された Python オブジェクトを再構成することができないということも意味します。
デフォルトでは、pickle
データフォーマットは比較的コンパクトなバイナリ表現を使用します。サイズの抑制目的の最適化が必要なら、pickel されたデータを効率的に 圧縮する ことができます。
pickletools
モジュールには pickle
によって生成されたデータストリームを解析するためのツールが含まれます。pickletools
のソースコードには、pickle プロトコルで使用される命令コードに関する詳細なコメントがあります。
現在 pickle 化には 6 種類のプロトコルを使用できます。より高いプロトコルを使用するほど、作成された pickle を読み込むためにより高い Python のバージョンが必要になります。
プロトコルバージョン 0 はオリジナルの「人間に判読可能な」プロトコルで、Python の初期のバージョンとの後方互換性を持ちます。
プロトコルバージョン 1 は旧形式のバイナリフォーマットで、これも Python の初期バージョンと互換性があります。
プロトコルバージョン 2 は Python 2.3 で導入されました。このバージョンでは 新方式のクラス のより効率的な pickle 化を提供しました。プロトコル 2 による改良に関する情報は PEP 307 を参照してください。
プロトコルバージョン 3 は Python 3 で追加されました。
bytes
オブジェクトを明示的にサポートしており、 Python 2.x で unpickle することはできません。これは Python 3.0から3.7のデフォルトプロトコルでした。プロトコルバージョン 4 は Python 3.4 で追加されました。このバージョンでは巨大なオブジェクトのサポート、より多くの種類のオブジェクトの pickle 化、および一部のデータ形式の最適化が行われました。Python 3.8からのデフォルトプロトコルです。プロトコル 4 による改良に関する情報は PEP 3154 を参照してください。
プロトコルバージョン 5 は Python 3.8 で追加されました。このバージョンでは帯域外データのサポートが追加され、また帯域内データに対するパフォーマンスが向上します。プロトコルバージョン 5 によってもたらされる改善についての情報は PEP 574 を参照してください。
注釈
直列化は永続性より原始的な概念です。 pickle
はファイルオブジェクトの読み書きを行いますが、永続オブジェクトの命名に関する問題にも、(さらに困難な) 永続オブジェクトへの並列アクセスに関する問題にも対応しません。pickle
モジュールは複雑なオブジェクトをバイトストリームに変換し、バイトストリームから同じ内部構造のオブジェクトに復元することができます。これらのバイトストリームはファイルに出力されることが多いでしょうが、ネットワークを介して送信したり、データベースに格納することもありえます。shelve
モジュールは、オブジェクトを DBM 方式のデータベースファイル上で pickle 化および非 pickle 化するシンプルなインターフェースを提供します。
モジュールインタフェース¶
オブジェクト階層を直列化するには、dumps()
関数を呼ぶだけです。同様に、データストリームを復元するには、loads()
関数を呼びます。しかし、直列化および復元に対してより多くのコントロールを行いたい場合、それぞれ Pickler
または Unpickler
オブジェクトを作成することができます。
pickle
モジュールは以下の定数を提供しています:
-
pickle.
HIGHEST_PROTOCOL
¶ 利用可能なうち最も高い プロトコルバージョン (整数)。この値は protocol 値として関数
dump()
およびdumps()
と、Pickler
コンストラクターに渡すことができます。
-
pickle.
DEFAULT_PROTOCOL
¶ pickle化に使われるデフォルトの プロトコルバージョン (整数)。`HIGHEST_PROTOCOL`よりも小さい場合があります。現在のデフォルトプロトコルは4です。このプロトコルはPython3.4で初めて導入され、その前のバージョンとは互換性がありません。
バージョン 3.0 で変更: デフォルトプロトコルは3です。
バージョン 3.8 で変更: デフォルトプロトコルは4です。
この pickle 化の手続きを便利にするために、 pickle
モジュールでは以下の関数を提供しています:
-
pickle.
dump
(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None)¶ オブジェクト obj を pickle 化し、すでにオープンしている ファイルオブジェクト file に書き込みます。
Pickler(file, protocol).dump(obj)
と等価です。引数 file, protocol, fix_imports および buffer_callback は
Pickler
のコンストラクタと同じ意味になります。バージョン 3.8 で変更: buffer_callback 引数が追加されました。
-
pickle.
dumps
(obj, protocol=None, *, fix_imports=True, buffer_callback=None)¶ ファイルに書く代わりに、
bytes
オブジェクトとしてオブジェクト obj の pickle 表現を返します。引数 protocol, fix_imports および buffer_callback は
Pickler
のコンストラクタと同じ意味になります。バージョン 3.8 で変更: buffer_callback 引数が追加されました。
-
pickle.
load
(file, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)¶ あるオブジェクトの pickle 表現を、オープンしている ファイルオブジェクト file から読み込み、その中で指定されているオブジェクト階層に再構成して返します。これは
Unpickler(file).load()
と等価です。pickle のプロトコルバージョンは自動的に検出されます。したがって protocol 引数は必要ありません。pickle 化オブジェクト表現より後のバイト列は無視されます。
引数 file, fix_imports, encoding, errors, strict および buffers は
Unpickler
のコンストラクタと同じ意味になります。バージョン 3.8 で変更: buffers 引数が追加されました。
-
pickle.
loads
(data, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)¶ オブジェクトのピックル化表現 data から再構成されたオブジェクト階層を返します。 data は バイトライクオブジェクト (bytes-like object) でなければなりません。
pickle のプロトコルバージョンは自動的に検出されます。したがって protocol 引数は必要ありません。pickle 化オブジェクト表現より後のバイト列は無視されます。
引数 file, fix_imports, encoding, errors, strict および buffers は
Unpickler
のコンストラクタと同じ意味になります。バージョン 3.8 で変更: buffers 引数が追加されました。
pickle
モジュールでは 3 つの例外を定義しています:
-
exception
pickle.
PicklingError
¶ Pickler
が pickle 化不可能なオブジェクトに遭遇したときに送出されるエラー。PickleError
を継承しています。どんな種類のオブジェクトが pickle 化できるのか確認するには pickle 化、非 pickle 化できるもの を参照してください。
-
exception
pickle.
UnpicklingError
¶ データ破損やセキュリティ違反のような、オブジェクトを非 pickle 化するのに問題がある場合に送出されるエラー。
PickleError
を継承します。非 picke 化の最中に他の例外が送出されることもあるので注意してください。これには AttributeError, EOFError, ImportError, IndexError が含まれます (ただし必ずしもこれらに限定されません)。
pickle
モジュールでは、3 つのクラス Pickler
, Unpickler
および Unpickler
を提供しています:
-
class
pickle.
Pickler
(file, protocol=None, *, fix_imports=True, buffer_callback=None)¶ pickle 化されたオブジェクトのデータストリームを書き込むためのバイナリファイルを引数にとります。
任意の引数 protocol は、整数で、pickle 化で使用するプロトコルを指定します; サポートされているプロトコルは 0 から
HIGHEST_PROTOCOL
までになります。指定されない場合、DEFAULT_PROTOCOL
が使用されます。負数が与えられた場合、HIGHEST_PROTOCOL
が使用されます。引数 file は、1 バイトの引数一つを受け付ける write() メソッドを持たなければなりません。すなわち、file には、バイナリの書き込み用にオープンされたファイルオブジェクト、
io.BytesIO
オブジェクト、このインタフェースに適合するその他のカスタムオブジェクトをとることができます。fix_imports が真であり、かつ、protocol が 3 未満の場合、pickle は新しい Python 3 の名前と Python 2 で使用されていた古いモジュール名との対応付けを試みるので、pickle データストリームは Python 2 でも読み込み可能です。
buffer_callback が None (デフォルト) の場合、バッファビューはストリームの一部として*file* 中に直列化されます。
buffer_callback が None でない場合、バッファビューを引数として何度でも呼び出すことができる関数です。コールバック関数が偽値 (None など) を返すと、与えられたバッファは アウトオブバウンド管理 (out-of-band) となります; そうでない場合はインバンドで、すなわち pickle ストリーム内で、直列化されます。
buffer_callback が None でなく、かつ protocol が None または 5 より小さい場合はエラーとなります。
バージョン 3.8 で変更: buffer_callback 引数が追加されました。
-
dump
(obj)¶ obj の pickle 化表現を、コンストラクターで与えられた、すでにオープンしているファイルオブジェクトに書き込みます。
-
persistent_id
(obj)¶ デフォルトでは何もしません。このメソッドはサブクラスがオーバーライドできるように存在します。
persistent_id()
がNone
を返す場合、通常通り obj が pickle 化されます。それ以外の値を返した場合、Pickler
がその値を obj のために永続的な ID として出力するようになります。この永続的な ID の意味はUnpickler.persistent_load()
によって定義されています。persistent_id()
によって返された値自身は永続的な ID を持つことができないことに注意してください。詳細および使用例については 外部オブジェクトの永続化 を参照してください。
-
dispatch_table
¶ pickler オブジェクトのディスパッチテーブルは
copyreg.pickle()
を使用して宣言できる種類の reduction functions のレジストリです。これはキーがクラスでその値が減少関数のマッピング型オブジェクトです。減少関数は関連するクラスの引数を 1 個とり、__reduce__()
メソッドと同じインタフェースでなければなりません。デフォルトでは、pickler オブジェクトは
dispatch_table
属性を持たず、代わりにcopyreg
モジュールによって管理されるグローバルなディスパッチテーブルを使用します。しかし、特定の pickler オブジェクトによる pickle 化をカスタマイズするためにdispatch_table
属性に dict-like オブジェクトを設定することができます。あるいは、Pickler
のサブクラスがdispatch_table
属性を持てば、そのクラスのインスタンスに対するデフォルトのディスパッチテーブルとして使用されます。使用例については ディスパッチテーブル を参照してください。
バージョン 3.3 で追加.
-
reducer_override
(self, obj)¶ Pickler
のサブクラスで定義可能な特殊なリデューサ (reducer) です。このメソッドはdispatch_table
内のいかなるリデューサよりも優先されます。このメソッドは__reduce__()
メソッドのインターフェースと適合していなければなりません。また、メソッドがNotImplemented
を返すことにより、dispatch_table
に登録されたリデューサにフォールバックしてobj
を直列化することもできます。詳細な例については、 型、関数、その他のオブジェクトに対するリダクションのカスタマイズ を参照してください。
バージョン 3.8 で追加.
-
fast
¶ 廃止予定です。真値が設定されれば高速モードを有効にします。高速モードは、メモの使用を無効にします。それにより余分な PUT 命令コードを生成しなくなるので pickle 化処理が高速化します。自己参照オブジェクトに対しては使用すべきではありません。さもなければ
Pickler
に無限再帰を起こさせるでしょう。よりコンパクトな pickle 化を必要とする場合は、
pickletools.optimize()
を使用してください。
-
-
class
pickle.
Unpickler
(file, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)¶ これは pickle データストリームの読み込みのためにバイナリファイルをとります。
pickle のプロトコルバージョンは自動的に検出されます。したがって protocol 引数は必要ありません。
引数 file は
io.BufferedIOBase
のインターフェースと同様に、整数を引数にとる read() 、バッファを引数にとる readinto() 、引数を取らない readline() の3つのメソッドを持たなければなりません。したがって、 file はバイナリ読み込みモードでオープンされたディスク上のファイル、io.BytesIO
オブジェクト、または上記インターフェース要件を満たす任意のカスタムオブジェクトのいずれかです。オプション引数 fix_imports, encoding および errors はPython 2で生成された pickle ストリームに対する互換性サポートを制御するために使われます。 fix_imports が真の場合、 pickle は古い Python 2 の名前を Python 3 の新しい名前に対応づけようとします。 encoding と errors は pickle に Python 2 で pickle 化された 8 ビット文字列をデコードする方法を指定します; これらの引数のデフォルト値はそれぞれ 'ASCII' と 'strict' です。 encoding は 8 ビット文字列インスタンスをバイトオブジェクトとして読み込む場合は 'bytes' を指定します。 Python 2 で pickle 化された NumPy 配列および
datetime
,date
,time
の各インスタンスを非 pickle 化するためにはencoding='latin1'
を使う必要があります。buffers が None (デフォルト値) の場合、非直列化に必要な全てのデータは pickle ストリームに含まれている必要があります。これは
Pickler
がインスタンス化されたとき (またはdump()
やdumps()
が呼び出されたとき) に buffer_callback 引数に None を指定したことに相当します。buffers が None でない場合、各イテレーションで アウトオブバウンド (out-of-band) のバッファビューを参照する pickle ストリームを消費する、バッファ対応のイテラブルでなければなりません。ここに指定するバッファは Pickler オブジェクトの buffer_callback に順番に渡されたものです。
バージョン 3.8 で変更: buffers 引数が追加されました。
-
load
()¶ コンストラクターで与えられたオープンしたファイルオブジェクトからオブジェクトの pickle 化表現を読み込み、その中で指定されたオブジェクト階層に再構成して返します。オブジェクトの pickle 化表現より後のバイト列は無視されます。
-
persistent_load
(pid)¶ デフォルトで
UnpicklingError
を送出します。もし定義されていれば、
persistent_load()
は永続的な ID pid によって指定されたオブジェクトを返す必要があります。永続的な ID が無効な場合、UnpicklingError
を送出しなければなりません。詳細および使用例については 外部オブジェクトの永続化 を参照してください。
-
find_class
(module, name)¶ 必要なら module をインポートして、そこから name という名前のオブジェクトを返します。ここで module および name 引数は
str
オブジェクトです。その名前が示唆することに反してfind_class()
は関数を探すためにも使われることに注意してください。サブクラスは、どんな型のオブジェクトを、どのようにロードするか (潜在的にはセキュリティリスクの減少) に関する制御を得るためにこれをオーバーライドすることができます。詳細に関しては グローバル変数を制限する を参照してください。
引数
modulet
,name
を指定して 監査イベントpickle.find_class
を送出します。
-
-
class
pickle.
PickleBuffer
(buffer)¶ pickle 可能なデータをあらわすバッファのラッパーです。 buffer はバッファライクなオブジェクト (bytes-like object) や N 次元配列のような バッファ機能を提供する オブジェクトでなければなりません。
PickleBuffer
はそれ自身バッファ機能を提供します。したがってこのクラスのインスタンスを、バッファ機能を提供するオブジェクトを期待するmemoryview
など他の API に渡すことが可能です。PickleBuffer
オブジェクトはプロトコル 5 以上でのみ直列化可能で、 アウトオブバウンド (out-of-band) の直列化 に対応しています。バージョン 3.8 で追加.
-
raw
()¶ このバッファの背後にあるメモリ領域への
memoryview
を返します。戻り値のオブジェクトはフォーマットB
(符号なしバイト) の C-連続な1次元のメモリビューです。バッファが C-連続でも Fortran-連続でもない場合BufferError
例外が送出されます。
-
release
()¶ PickleBuffer オブジェクトを通じてアクセスされる背後のバッファを解放します。
-
pickle 化、非 pickle 化できるもの¶
以下の型は pickle 化できます:
None
、True
、およびFalse
整数、浮動小数点数、複素数
文字列、バイト列、バイト配列
pickle 化可能なオブジェクトからなるタプル、リスト、集合および辞書
モジュールのトップレベルで定義されている組込み関数
モジュールのトップレベルで定義されているクラス
__dict__
属性を持つクラス、あるいは__getstate__()
メソッドの返り値が pickle 化可能なクラス (詳細は クラスインスタンスの pickle 化 を参照)。
pickle 化できないオブジェクトを pickle 化しようとすると、PicklingError
例外が送出されます。この例外が起きたとき、すでに元のファイルには未知の長さのバイト列が書き込まれている場合があります。極端に再帰的なデータ構造を pickle 化しようとした場合には再帰の深さ制限を越えてしまうかもしれず、この場合には RecursionError
が送出されます。この制限は、sys.setrecursionlimit()
で慎重に上げていくことは可能です。
関数 (組込みおよびユーザー定義) は、値ではなく、"完全修飾" された名前参照で pickle 化されます。2 これは関数が定義されたモジュールをともにした関数名のみが pickle 化されることを意味します。関数のコードやその属性は pickle 化されません。すなわち、非 pickle 化する環境で定義したモジュールがインポート可能な状態になっており、そのモジュール内に関数名のオブジェクトが含まれていなければなりません。この条件を満たさなかった場合は例外が送出されます。3
クラスも同様に名前参照で pickle 化されるので、unpickle 化環境には同じ制限が課せられます。クラス中のコードやデータは何も pickle 化されないので、以下の例ではクラス属性 attr
が unpickle 化環境で復元されないことに注意してください
class Foo:
attr = 'A class attribute'
picklestring = pickle.dumps(Foo)
pickle 化可能な関数やクラスがモジュールのトップレベルで定義されていなければならないのはこれらの制限のためです。
同様に、クラスのインスタンスが pickle 化された際、そのクラスのコードおよびデータはオブジェクトと一緒に pickle 化されることはありません。インスタンスのデータのみが pickle 化されます。この仕様は、クラス内のバグを修正したりメソッドを追加した後でも、そのクラスの以前のバージョンで作られたオブジェクトを読み出せるように意図的に行われています。あるクラスの多くのバージョンで使われるような長命なオブジェクトを作ろうと計画しているなら、そのクラスの __setstate__()
メソッドによって適切な変換が行われるようにオブジェクトのバージョン番号を入れておくとよいかもしれません。
クラスインスタンスの pickle 化¶
この節では、クラスインスタンスがどのように pickle 化または非 pickle 化されるのかを定義したり、カスタマイズしたり、コントロールしたりするのに利用可能な一般的機構について説明します。
ほとんどの場合、インスタンスを pickle 化できるようにするために追加のコードは必要ありません。デフォルトで、pickle はインスタンスのクラスと属性を内省によって検索します。クラスインスタンスが非 pickle 化される場合、通常その __init__()
メソッドは実行 されません 。デフォルトの振る舞いは、最初に初期化されていないインスタンスを作成して、次に保存された属性を復元します。次のコードはこの振る舞いの実装を示しています:
def save(obj):
return (obj.__class__, obj.__dict__)
def load(cls, attributes):
obj = cls.__new__(cls)
obj.__dict__.update(attributes)
return obj
クラスは、いくつかの特殊メソッドを提供することによって、デフォルトの振る舞いを変更することができます:
-
object.
__getnewargs_ex__
()¶ プロトコル 2 以上の場合、
__getnewargs_ex__()
メソッドを実装したクラスは__new__()
メソッドに渡された値の非 pickle 化の方法を指示することができます。このメソッドは、オブジェクトの生成に必要な位置引数のタプル args と名前付き引数の辞書 kwargs のペア(args, kwargs)
を返さなければなりません。これらは非 pickle 化に際して__new__()
メソッドに渡されます。クラスの
__new__()
メソッドがキーワード専用引数を求める場合はこのメソッドを実装すべきです。 そうしない場合、互換性のため__getnewargs__()
メソッドの実装を推奨します。バージョン 3.6 で変更:
__getnewargs_ex__()
がプロトコル 2 と 3 でも使われるようになりました。
-
object.
__getnewargs__
()¶ このメソッドは
__getnewargs_ex__()
と同じような機能を提供しますが、位置引数のみをサポートします。このメソッドは引数のタプルargs
を返さなければならず、戻り値は非 pickle 化に際して__new__()
メソッドに渡されます。__getnewargs_ex__()
が定義されていると__getnewargs__()
は呼び出しません。バージョン 3.6 で変更: Python 3.6以前のプロトコル 2 と 3 では、
__getnewargs_ex__()
の代わりに__getnewargs__()
が呼び出されていました。
-
object.
__getstate__
()¶ クラスはそのインスタンスをどう pickle 化するかについてさらに影響を与えることができます; クラスに
__getstate__()
メソッドが定義されていた場合それが呼ばれ、返り値のオブジェクトはインスタンスの辞書ではなく、インスタンスの内容が pickle 化されたものになります。__getstate__()
がないときは通常通りインスタンスの__dict__
が pickle 化されます。
-
object.
__setstate__
(state)¶ 非 pickle 化に際して、クラスが
__setstate__()
を定義している場合、それは非 pickle 化された状態とともに呼び出されます。その場合、状態オブジェクトが辞書でなければならないという要求はありません。そうでなければ、 pickle された状態は辞書で、その要素は新しいインスタンスの辞書に割り当てられます。注釈
__getstate__()
が偽値を返す場合、非 pickle 化時に__setstate__()
メソッドは呼ばれません。
__getstate__()
および __setstate__()
メソッドの使い方に関する詳細な情報については 状態を持つオブジェクトの扱い 節を参照してください。
注釈
非 pickle 化に際しては、 __getattr__()
, __getattribute__()
, または __setattr__()
といったメソッドがインスタンスに対して呼ばれることがあります。これらのメソッドが何らかの内部の不変な条件が真であることを必要とする場合、その型は __new__()
メソッドを実装してそのような不変な条件を構築すべきです。なぜならばインスタンスの非 pickle 化においては __init__()
メソッドは呼ばれないからです。
これらから見るように、pickle は上記のメソッドを直接使用しません。実際には、これらのメソッドは __reduce__()
特殊メソッドを実装するコピープロトコルの一部です。コピープロトコルは、pickle 化とオブジェクトのコピーに必要な、データを取得するための統一されたインタフェースを提供します。 4
強力ですが、クラスに __reduce__()
メソッドを直接実装することはエラーを起こしやすくなります。この理由のため、クラスの設計者は可能なかぎり高レベルインタフェース (__getnewargs_ex__()
、__getstate__()
および __setstate__()
) を使用するべきです。公開はしているものの、__reduce__()
の使用は、あくまでオプションとして、より効果的な pickle 化につながる場合、あるいはその両方の場合のみにしてください。
-
object.
__reduce__
()¶ このインタフェースは現在、以下のように定義されています。
__reduce__()
メソッドは引数を取らず、文字列あるいは (こちらの方が好まれますが) タプルのいずれかを返すべきです (返されたオブジェクトは、しばしば "reduce value" と呼ばれます)。文字列が返された場合、その文字列はグローバル変数の名前として解釈されます。それはオブジェクトのモジュールから見たローカル名であるべきです; pickle モジュールは、オブジェクトのモジュールを決定するためにモジュールの名前空間を検索します。この振る舞いは、典型的にシングルトンで便利です。
タプルが返された場合、それは 2〜6 要素長でなければなりません。オプションのアイテムは省略することができます。あるいはそれらの値として
None
を渡すことができます。各要素の意味は順に:オブジェクトの初期バージョンを作成するために呼ばれる呼び出し可能オブジェクト。
呼出し可能オブジェクトに対する引数のタプル。呼出し可能オブジェクトが引数を受け取らない場合、空のタプルが与えられなければなりません。
任意で、前述のオブジェクトの
__setstate__()
メソッドに渡されるオブジェクトの状態。オブジェクトがそのようなメソッドを持たない場合、値は辞書でなければならず、それはオブジェクトの__dict__
属性に追加されます。任意で、連続した要素を yield する (シーケンスではなく) イテレーター。これらの要素は
obj.append(item)
を使用して、あるいはバッチではobj.extend(list_of_items)
を使用して、オブジェクトに追加されます。これは主としてリストのサブクラスに対して使用されますが、適切なシグネチャを持つappend()
およびextend()
メソッドがあるかぎり、他のクラスで使用することもできます。 (append()
またはextend()
のどちらが使用されるかは、どの pickle プロトコルバージョンが使われるかに加えて追加されるアイテムの数にも依存します。したがって、両方をサポートする必要があります)任意で、連続する key-value ペアを yield する (シーケンスでなく) イテレーター。これらの要素は
obj[key] = value
を使用して、オブジェクトに格納されます。これは主として辞書のサブクラスに対して使用されますが、__setitem__()
を実装しているかぎり他のクラスで使用することもできます。任意で、シグネチャが
(obj, state)
である呼び出し可能オブジェクト。このオブジェクトは、obj
のスタティックな__setstate__()
メソッドの代わりに、ユーザーがオブジェクトの状態を更新する方法をプログラム的に制御することを許します。None
以外の場合、この呼び出し可能オブジェクトはobj
の__setstate__()
メソッドに優先します。バージョン 3.8 で追加: 任意の6番目のタプル要素
(obj, state)
が追加されました。
-
object.
__reduce_ex__
(protocol)¶ 別の方法として、
__reduce_ex__()
メソッドを定義することもできます。唯一の違いは、このメソッドは単一の整数引数、プロトコルバージョンを取る必要があるということです。もし定義された場合、pickle は__reduce__()
メソッドよりもこのメソッドを優先します。さらに、__reduce__()
は自動的に拡張版の同義語になります。このメソッドの主な用途は、古い Python リリースに対して後方互換性のある reduce value を提供することです。
外部オブジェクトの永続化¶
オブジェクトの永続化のために、pickle
モジュールは、pickle データストリーム外のオブジェクトに対する参照の概念をサポートしています。そのようなオブジェクトは永続的 ID によって参照されます。それは、英数文字の文字列 (プロトコル 0 に対して) 5 あるいは単に任意のオブジェクト (より新しい任意のプロトコルに対して) のいずれかです。
そのような永続的 ID の分解能は pickle
モジュールでは定義されていません; これはこの分解能を pickler および unpickler のそれぞれ persistent_id()
および persistent_load()
上でのユーザー定義メソッドに移譲します。
外部の永続的 ID を持つ pickle オブジェクトの pickler は、引数にオブジェクトを取り、None
かオブジェクトの永続的 ID を返すカスタム persistent_id()
メソッドを持たなくてはなりません。None
を返す場合、pickler は通常通りマーカーとともにオブジェクトを pickle 化するため、unpickler はそれを永続的 ID として認識します。
外部オブジェクトを非 pickle 化するには、unpickler は永続的 ID オブジェクトを取り被参照オブジェクトを返すカスタム persistent_load()
メソッドを持たなくてはなりません。
これは、外部のオブジェクトを参照によって pickle 化するために永続的 ID をどのように使用するかを示す包括的な例です。
# Simple example presenting how persistent ID can be used to pickle
# external objects by reference.
import pickle
import sqlite3
from collections import namedtuple
# Simple class representing a record in our database.
MemoRecord = namedtuple("MemoRecord", "key, task")
class DBPickler(pickle.Pickler):
def persistent_id(self, obj):
# Instead of pickling MemoRecord as a regular class instance, we emit a
# persistent ID.
if isinstance(obj, MemoRecord):
# Here, our persistent ID is simply a tuple, containing a tag and a
# key, which refers to a specific record in the database.
return ("MemoRecord", obj.key)
else:
# If obj does not have a persistent ID, return None. This means obj
# needs to be pickled as usual.
return None
class DBUnpickler(pickle.Unpickler):
def __init__(self, file, connection):
super().__init__(file)
self.connection = connection
def persistent_load(self, pid):
# This method is invoked whenever a persistent ID is encountered.
# Here, pid is the tuple returned by DBPickler.
cursor = self.connection.cursor()
type_tag, key_id = pid
if type_tag == "MemoRecord":
# Fetch the referenced record from the database and return it.
cursor.execute("SELECT * FROM memos WHERE key=?", (str(key_id),))
key, task = cursor.fetchone()
return MemoRecord(key, task)
else:
# Always raises an error if you cannot return the correct object.
# Otherwise, the unpickler will think None is the object referenced
# by the persistent ID.
raise pickle.UnpicklingError("unsupported persistent object")
def main():
import io
import pprint
# Initialize and populate our database.
conn = sqlite3.connect(":memory:")
cursor = conn.cursor()
cursor.execute("CREATE TABLE memos(key INTEGER PRIMARY KEY, task TEXT)")
tasks = (
'give food to fish',
'prepare group meeting',
'fight with a zebra',
)
for task in tasks:
cursor.execute("INSERT INTO memos VALUES(NULL, ?)", (task,))
# Fetch the records to be pickled.
cursor.execute("SELECT * FROM memos")
memos = [MemoRecord(key, task) for key, task in cursor]
# Save the records using our custom DBPickler.
file = io.BytesIO()
DBPickler(file).dump(memos)
print("Pickled records:")
pprint.pprint(memos)
# Update a record, just for good measure.
cursor.execute("UPDATE memos SET task='learn italian' WHERE key=1")
# Load the records from the pickle data stream.
file.seek(0)
memos = DBUnpickler(file, conn).load()
print("Unpickled records:")
pprint.pprint(memos)
if __name__ == '__main__':
main()
ディスパッチテーブル¶
pickle 化に依存する他のコードの邪魔をせずに、一部のクラスの pickle 化だけをカスタマイズしたい場合、プライベートのディスパッチテーブルを持つ pickler を作成することができます。
copyreg
モジュールによって管理されるグローバルなディスパッチテーブルは copyreg.dispatch_table
として利用可能です。したがって、copyreg.dispatch_table
の修正済のコピーをプライベートのディスパッチテーブルとして使用することを選択できます。
例えば
f = io.BytesIO()
p = pickle.Pickler(f)
p.dispatch_table = copyreg.dispatch_table.copy()
p.dispatch_table[SomeClass] = reduce_SomeClass
これは SomeClass
クラスを特別に扱うプライベートのディスパッチテーブルを持つ pickle.Pickler
のインスタンスを作成します。あるいは、次のコード
class MyPickler(pickle.Pickler):
dispatch_table = copyreg.dispatch_table.copy()
dispatch_table[SomeClass] = reduce_SomeClass
f = io.BytesIO()
p = MyPickler(f)
も同じことをしますが、 MyPickler
のすべてのインスタンスはデフォルトで同じディスパッチテーブルを共有します。 copyreg
モジュールを使用する等価なコードは
copyreg.pickle(SomeClass, reduce_SomeClass)
f = io.BytesIO()
p = pickle.Pickler(f)
状態を持つオブジェクトの扱い¶
ここでは、クラスを pickle 化する振る舞いの変更手順を紹介しています。TextReader
クラスはテキストファイルをオープンし、readline()
メソッドが呼ばれると、その度に行番号と行の内容を返します。TextReader
インスタンスが pickle 化されるとき、ファイルオブジェクトメンバーを 除く すべての属性が保存されます。インスタンスが非 pickle 化されるとき、ファイルは再びオープンされ、最後に読み込んだ位置から読み込みを再開します。このような振る舞いを実装するには __setstate__()
および __getstate__()
メソッドを使用します。
class TextReader:
"""Print and number lines in a text file."""
def __init__(self, filename):
self.filename = filename
self.file = open(filename)
self.lineno = 0
def readline(self):
self.lineno += 1
line = self.file.readline()
if not line:
return None
if line.endswith('\n'):
line = line[:-1]
return "%i: %s" % (self.lineno, line)
def __getstate__(self):
# Copy the object's state from self.__dict__ which contains
# all our instance attributes. Always use the dict.copy()
# method to avoid modifying the original state.
state = self.__dict__.copy()
# Remove the unpicklable entries.
del state['file']
return state
def __setstate__(self, state):
# Restore instance attributes (i.e., filename and lineno).
self.__dict__.update(state)
# Restore the previously opened file's state. To do so, we need to
# reopen it and read from it until the line count is restored.
file = open(self.filename)
for _ in range(self.lineno):
file.readline()
# Finally, save the file.
self.file = file
使用例は以下のようになるでしょう:
>>> reader = TextReader("hello.txt")
>>> reader.readline()
'1: Hello world!'
>>> reader.readline()
'2: I am line number two.'
>>> new_reader = pickle.loads(pickle.dumps(reader))
>>> new_reader.readline()
'3: Goodbye!'
型、関数、その他のオブジェクトに対するリダクションのカスタマイズ¶
バージョン 3.8 で追加.
dispatch_table
は、ときにその柔軟性が十分でないがことあります。特に、オブジェクトの型以外の別の条件で pickle 化をカスタマイズしたい場合や、関数やクラスを使って pickle 化をカスタマイズしたい場合などです。
そのような場合、 Pickler
クラスから派生したサブクラスで reducer_override()
メソッドを実装することができます。このメソッドは任意のリダクション用タプルを返すことができます (__reduce__()
を参照してください)。もしくは、従来の振る舞いにフォールバックするために NotImplemented
を返すこともできます。
dispatch_table
と reducer_override()
の両方が定義されている場合、 reducer_override()
メソッドが優先されます。
注釈
パフォーマンス上の理由により、次に挙げるオブジェクトに対しては reducer_override()
が呼ばれないことがあります: None
, True
, False
, および int
, float
, bytes
, str
, dict
, set
, frozenset
, list
, tuple
の厳密なインスタンス。
以下は特定のクラスを pickle 化して再構成する単純な例です:
import io
import pickle
class MyClass:
my_attribute = 1
class MyPickler(pickle.Pickler):
def reducer_override(self, obj):
"""Custom reducer for MyClass."""
if getattr(obj, "__name__", None) == "MyClass":
return type, (obj.__name__, obj.__bases__,
{'my_attribute': obj.my_attribute})
else:
# For any other object, fallback to usual reduction
return NotImplemented
f = io.BytesIO()
p = MyPickler(f)
p.dump(MyClass)
del MyClass
unpickled_class = pickle.loads(f.getvalue())
assert isinstance(unpickled_class, type)
assert unpickled_class.__name__ == "MyClass"
assert unpickled_class.my_attribute == 1
アウトオブバウンドバッファ¶
バージョン 3.8 で追加.
ある状況では、 pickle
モジュールは大量のデータを転送するために使われます。 そのため、メモリのコピーを最小限に抑えてパフォーマンスとリソースの消費を良好な状態に保つことが重要になることがあります。 しかし、オブジェクトのグラフ的構造をシーケンシャルなバイトストリームに変換する pickle
モジュールの通常の処理は、本質的に pickle ストリームへの、または pickle ストリームからのデータのコピーを伴います。
この制約は、生産者 provider (変換されるオブジェクトの型の実装) と消費者 consumer (通信システムの実装) が pickle プロトコル5以上で提供されるアウトオブバウンドのデータ転送機能をサポートしていれば回避できます。
生産者 API¶
pickle 化される大きなサイズのデータオブジェクトは、プロトコル5以上でサポートされた __reduce_ex__()
メソッドを実装しなければなりません。このメソッドは大きなデータに対して (bytes
オブジェクトなどの代わりに) PickleBuffer
インスタンスを返します。
PickleBuffer
オブジェクトは背後にあるバッファがアウトオブバウンドのデータ転送に適合していることを 知らせます 。これらのオブジェクトは pickle
モジュールの通常の使い方との互換性を保っています。しかし、消費者側で pickle
モジュールに対してそれらのバッファを自身で処理することを事前に知らせることもできます。
消費者 API¶
通信システムは、オブジェクトグラフを直列化するときに生成された PickleBuffer
オブジェクトのカスタマイズされた処理を有効化することができます。
送信側は buffer_callback 引数を Pickler
(または dump()
や dumps()
関数) に渡す必要があります。この関数はオブジェクトグラフを pickle 化するときに生成されるそれぞれの PickleBuffer
を引数として呼ばれます。 buffer_callback によって蓄積されたバッファは、それが保持するデータのコピーを pickle ストリームに送らず、軽量なマーカーが挿入されるだけです。
受信側は buffers 引数を Unpickler
(または load()
や loads()
関数) に渡す必要があります。これは buffer_callback に渡されたバッファのイテラブルです。このイテラブルは buffer_callback に渡されたのと同じ順番でバッファを返さなければなりません。これらのバッファは、pickle 化処理によって PickleBuffer
オブジェクトを生成したオブジェクトの再構築処理で期待されるデータを提供します。
送信側と受信側の間で、通信システムはアウトオブバウンドバッファの独自の転送メカニズムを自由に実装することができます。見込みのある最適化としては、共有メモリの利用や、データタイプ依存のデータ圧縮などが考えられます。
使用例¶
以下は、アウトオブバウンドのバッファを使った pickle 処理に関与することができるサブクラス bytearray
を実装したささいな例です:
class ZeroCopyByteArray(bytearray):
def __reduce_ex__(self, protocol):
if protocol >= 5:
return type(self)._reconstruct, (PickleBuffer(self),), None
else:
# PickleBuffer is forbidden with pickle protocols <= 4.
return type(self)._reconstruct, (bytearray(self),)
@classmethod
def _reconstruct(cls, obj):
with memoryview(obj) as m:
# Get a handle over the original buffer object
obj = m.obj
if type(obj) is cls:
# Original buffer object is a ZeroCopyByteArray, return it
# as-is.
return obj
else:
return cls(obj)
再構成関数 (_reconstruct
クラスメソッド) は、受け取ったバッファが持っているオブジェクトを、それが正しい型であれば、そのまま返します。これは、このおもちゃのような例において、ゼロコピーの挙動を模擬的に行う簡単な方法です。
消費者側では、これらのオブジェクトを通常の方法で pickle 化することができます。この場合非直列化処理は元のオブジェクトのコピーを返します:
b = ZeroCopyByteArray(b"abc")
data = pickle.dumps(b, protocol=5)
new_b = pickle.loads(data)
print(b == new_b) # True
print(b is new_b) # False: a copy was made
いっぽう直列化において buffer_callback を設定し、非直列化において蓄積されたバッファを渡した場合、コピーではなく元のオブジェクトを得ることができます:
b = ZeroCopyByteArray(b"abc")
buffers = []
data = pickle.dumps(b, protocol=5, buffer_callback=buffers.append)
new_b = pickle.loads(data, buffers=buffers)
print(b == new_b) # True
print(b is new_b) # True: no copy was made
この例では bytearray
がそれ自身メモリを割り当てるという性質による制限があります: すなわち、他のオブジェクトのメモリを参照する bytearray
を生成することはできません。しかし、 NumPy 配列のようなサードパーティのデータ型ではそのような制限はなく、異なるプロセス間または異なるシステム間で、ゼロコピー (または最小限のコピー) でのpickle 処理の利用が可能です。
参考
PEP 574 -- Pickle プロトコルバージョン 5 による帯域外データ
グローバル変数を制限する¶
デフォルトで、非 pickle 化は pickle データ内で見つけたあらゆるクラスや関数をインポートします。多くのアプリケーションでは、この振る舞いは受け入れられません。なぜなら、それによって unpickler が任意のコードをインポートして実行することが可能になるからです。この手の巧妙に作られた pickle データストリームがロードされたときに何を行うかをちょっと考えてみてください:
>>> import pickle
>>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
hello world
0
この例において、unpickler は os.system()
関数をインポートして、次に文字列の引数 "echo hello world" を適用しています。この例は無害ですが、システムを破壊する例を想像するのは難しくありません。
この理由のため、Unpickler.find_class()
をカスタマイズすることで非 pickle 化で何を得るかを制御したくなるかもしれません。その名前が示唆するのと異なり、Unpickler.find_class()
はグローバル (クラスや関数) が必要とした時にはいつでも呼びだされます。したがって、グローバルを完全に禁止することも安全なサブセットに制限することも可能です。
これは、一部の安全なクラスについてのみ builtins
モジュールからロードすることを許可する unpickler の例です:
import builtins
import io
import pickle
safe_builtins = {
'range',
'complex',
'set',
'frozenset',
'slice',
}
class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module, name):
# Only allow safe classes from builtins.
if module == "builtins" and name in safe_builtins:
return getattr(builtins, name)
# Forbid everything else.
raise pickle.UnpicklingError("global '%s.%s' is forbidden" %
(module, name))
def restricted_loads(s):
"""Helper function analogous to pickle.loads()."""
return RestrictedUnpickler(io.BytesIO(s)).load()
この unpickler が働く使用例は次のように意図されます:
>>> restricted_loads(pickle.dumps([1, 2, range(15)]))
[1, 2, range(0, 15)]
>>> restricted_loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
Traceback (most recent call last):
...
pickle.UnpicklingError: global 'os.system' is forbidden
>>> restricted_loads(b'cbuiltins\neval\n'
... b'(S\'getattr(__import__("os"), "system")'
... b'("echo hello world")\'\ntR.')
Traceback (most recent call last):
...
pickle.UnpicklingError: global 'builtins.eval' is forbidden
この例が示すように、非 pickle 化を認めるものに注意しなければなりません。したがって、セキュリティが重要な場合は xmlrpc.client
の marshal API や、サードパーティのソリューションのような別の選択肢を考慮した方がよいでしょう。
性能¶
pickle プロトコルの最近のバージョン (プロトコル 2 以降) は一部の一般的な機能と組み込みデータ型を効率的にバイナリにエンコードするよう考慮されています。また、pickle
モジュールは C 言語で書かれた透過的オプティマイザーを持っています。
使用例¶
最も単純なコードでは、dump()
および load()
関数を使用してください。
import pickle
# An arbitrary collection of objects supported by pickle.
data = {
'a': [1, 2.0, 3, 4+6j],
'b': ("character string", b"byte string"),
'c': {None, True, False}
}
with open('data.pickle', 'wb') as f:
# Pickle the 'data' dictionary using the highest protocol available.
pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)
次の例は、pickle 化されたデータを読み込みます。
import pickle
with open('data.pickle', 'rb') as f:
# The protocol version used is detected automatically, so we do not
# have to specify it.
data = pickle.load(f)
参考
脚注
- 1
marshal
モジュールと間違えないように注意してください。- 2
なぜ
lambda
関数を pickle 化できないかというと、すべてのlambda
関数は同じ名前:<lambda>
を共有しているからです。- 3
送出される例外は
ImportError
やAttributeError
になるはずですが、他の例外も起こりえます。- 4
copy
モジュールは、浅いコピーと深いコピーの操作にこのプロトコルを使用します。- 5
英数文字に関する制限は、プロトコル 0 では永続的な ID が改行文字によって区切られるという事実によります。そのため、永続的な ID に何らかの改行文字が含まれると、結果として生じる pickle は判読不能になります。