pickle —Pythonオブジェクトのシリアル化
ソースコード: :source: `Lib / pickle.py`
pickle モジュールは、Pythonオブジェクト構造をシリアル化および逆シリアル化するためのバイナリプロトコルを実装します。 「Pickling」はPythonオブジェクト階層をバイトストリームに変換するプロセスであり、「unpickling」は逆の操作であり、バイトストリーム(バイナリから) file または bytes-like object )は、オブジェクト階層に変換されます。 酸洗い(および酸洗い解除)は、「シリアル化」、「マーシャリング」、 1 、または「フラット化」とも呼ばれます。 ただし、混乱を避けるために、ここで使用される用語は「酸洗い」と「酸洗い解除」です。
警告
pickle
モジュールは安全ではありません。 信頼できるデータのみを選択解除します。
ピクルス解除中に任意のコードを実行する悪意のあるピクルスデータを構築する可能性があります。 信頼できないソースから取得された可能性のあるデータや、改ざんされた可能性のあるデータを盗み出さないでください。
データが改ざんされていないことを確認する必要がある場合は、 hmac を使用してデータに署名することを検討してください。
信頼できないデータを処理する場合は、 json などのより安全なシリアル化形式の方が適切な場合があります。 json との比較を参照してください。
他のPythonモジュールとの関係
marshalとの比較
Pythonには marshal と呼ばれるより原始的なシリアル化モジュールがありますが、一般に pickle は常にPythonオブジェクトをシリアル化するための推奨される方法です。 marshal は、主にPythonの.pyc
ファイルをサポートするために存在します。
pickle モジュールは、 marshal といくつかの重要な点で異なります。
pickle モジュールは、すでにシリアル化されているオブジェクトを追跡するため、後で同じオブジェクトへの参照が再度シリアル化されることはありません。 marshal はこれを行いません。
これは、再帰オブジェクトとオブジェクト共有の両方に影響します。 再帰オブジェクトは、それ自体への参照を含むオブジェクトです。 これらはマーシャルによって処理されません。実際、再帰オブジェクトをマーシャリングしようとすると、Pythonインタープリターがクラッシュします。 オブジェクト共有は、シリアル化されるオブジェクト階層の異なる場所に同じオブジェクトへの複数の参照がある場合に発生します。 pickle は、そのようなオブジェクトを1回だけ格納し、他のすべての参照がマスターコピーを指すようにします。 共有オブジェクトは共有されたままです。これは、可変オブジェクトにとって非常に重要な場合があります。
marshal を使用して、ユーザー定義クラスとそのインスタンスをシリアル化することはできません。 pickle は、クラスインスタンスを透過的に保存および復元できますが、クラス定義はインポート可能であり、オブジェクトが保存されたときと同じモジュールに存在する必要があります。
marshal シリアル化形式は、Pythonバージョン間での移植性が保証されていません。 人生の主な仕事は
.pyc
ファイルをサポートすることであるため、Pythonの実装者は、必要に応じて下位互換性のない方法でシリアル化形式を変更する権利を留保します。 pickle シリアル化形式は、互換性のあるpickleプロトコルが選択され、データがその固有の重大な変更言語と交差している場合、pickle化およびunpicklingコードがPython2からPython3のタイプの違いを処理する場合、Pythonリリース間で下位互換性が保証されます。境界。
jsonとの比較
ピクルスプロトコルと JSON(JavaScript Object Notation)には基本的な違いがあります。
- JSONはテキストシリアル化形式です(ほとんどの場合、
utf-8
にエンコードされますが、ユニコードテキストを出力します)が、pickleはバイナリシリアル化形式です。 - JSONは人間が読める形式ですが、pickleはそうではありません。
- JSONは相互運用可能であり、Pythonエコシステムの外部で広く使用されていますが、pickleはPython固有です。
- JSONは、デフォルトでは、Python組み込み型のサブセットのみを表すことができ、カスタムクラスは表すことができません。 pickleは、非常に多くのPythonタイプを表すことができます(それらの多くは、Pythonのイントロスペクション機能を巧妙に使用することで自動的に実行されます。複雑なケースは、固有のオブジェクトAPI を実装することで対処できます)。
- pickleとは異なり、信頼できないJSONを逆シリアル化しても、それ自体が任意のコード実行の脆弱性を作成することはありません。
データストリーム形式
pickle で使用されるデータ形式はPython固有です。 これには、JSONやXDR(ポインター共有を表すことができない)などの外部標準によって課せられる制限がないという利点があります。 ただし、これは、Python以外のプログラムがpickle化されたPythonオブジェクトを再構築できない可能性があることを意味します。
デフォルトでは、 pickle データ形式は比較的コンパクトなバイナリ表現を使用します。 最適なサイズ特性が必要な場合は、ピクルス化されたデータを効率的に圧縮できます。
モジュール pickletools には、 pickle によって生成されたデータストリームを分析するためのツールが含まれています。 pickletools ソースコードには、pickleプロトコルで使用されるオペコードに関する広範なコメントがあります。
現在、酸洗いに使用できる6つの異なるプロトコルがあります。 使用するプロトコルが高ければ高いほど、生成されたピクルスを読み取るために必要なPythonのバージョンは新しくなります。
- プロトコルバージョン0は、元の「人間が読める」プロトコルであり、以前のバージョンのPythonと下位互換性があります。
- プロトコルバージョン1は、以前のバージョンのPythonとも互換性のある古いバイナリ形式です。
- プロトコルバージョン2はPython2.3で導入されました。 新しいスタイルのクラス esのはるかに効率的な酸洗いを提供します。 プロトコル2によってもたらされる改善については、 PEP 307 を参照してください。
- プロトコルバージョン3がPython3.0で追加されました。 bytes オブジェクトを明示的にサポートしており、Python2.xで選択を解除することはできません。 これは、Python 3.0〜3.7のデフォルトのプロトコルでした。
- プロトコルバージョン4はPython3.4で追加されました。 これにより、非常に大きなオブジェクトのサポート、より多くの種類のオブジェクトのピクルス化、およびいくつかのデータ形式の最適化が追加されます。 これは、Python3.8以降のデフォルトのプロトコルです。 プロトコル4によってもたらされる改善については、 PEP 3154 を参照してください。
- プロトコルバージョン5がPython3.8で追加されました。 帯域外データのサポートと帯域内データの高速化を追加します。 プロトコル5によってもたらされる改善については、 PEP 574 を参照してください。
ノート
シリアル化は、永続性よりも原始的な概念です。 pickle はファイルオブジェクトの読み取りと書き込みを行いますが、永続オブジェクトの命名の問題や、永続オブジェクトへの同時アクセスの(さらに複雑な)問題は処理しません。 pickle モジュールは、複雑なオブジェクトをバイトストリームに変換し、バイトストリームを同じ内部構造を持つオブジェクトに変換できます。 おそらく、これらのバイトストリームで最も明白なことは、ファイルに書き込むことですが、ネットワークを介して送信したり、データベースに保存したりすることも考えられます。 shelve モジュールは、DBMスタイルのデータベースファイル上のオブジェクトをピクルスおよびピクルス解除するためのシンプルなインターフェイスを提供します。
モジュールインターフェース
オブジェクト階層をシリアル化するには、 dumps()関数を呼び出すだけです。 同様に、データストリームを逆シリアル化するには、 loads()関数を呼び出します。 ただし、シリアル化と逆シリアル化をより細かく制御したい場合は、それぞれ Pickler または Unpickler オブジェクトを作成できます。
pickle モジュールは、次の定数を提供します。
- pickle.HIGHEST_PROTOCOL
- 整数、利用可能な最高のプロトコルバージョン。 この値は、プロトコル値として関数 dump()と dumps()、および Pickler コンストラクターに渡すことができます。
- pickle.DEFAULT_PROTOCOL
整数。ピクルスに使用されるデフォルトのプロトコルバージョン。 HIGHEST_PROTOCOL よりも小さい場合があります。 現在、デフォルトのプロトコルは4で、Python 3.4で最初に導入され、以前のバージョンと互換性がありません。
バージョン3.0で変更:デフォルトのプロトコルは3です。
バージョン3.8で変更:デフォルトのプロトコルは4です。
pickle モジュールは、酸洗いプロセスをより便利にするために次の機能を提供します。
- pickle.dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None)
オブジェクト obj のpickle化された表現を開いているファイルオブジェクト ファイルに書き込みます。 これは
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)
オブジェクト obj のpickle化された表現を、ファイルに書き込むのではなく、 bytes オブジェクトとして返します。
引数 protocol 、 fix_imports 、および buffer_callback は、 Pickler コンストラクターと同じ意味を持ちます。
バージョン3.8で変更: buffer_callback 引数が追加されました。
- pickle.load(file, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)
開いているファイルオブジェクト ファイルからオブジェクトのpickle化された表現を読み取り、そこで指定されている再構成されたオブジェクト階層を返します。 これは
Unpickler(file).load()
と同等です。ピクルスのプロトコルバージョンは自動的に検出されるため、プロトコル引数は必要ありません。 オブジェクトのpickle化された表現を超えたバイトは無視されます。
引数ファイル、 fix_imports 、エンコーディング、エラー、厳密およびバッファーには Unpickler コンストラクターと同じ意味です。
バージョン3.8で変更: buffers 引数が追加されました。
- pickle.loads(data, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)
オブジェクトのpickle化された表現 data の再構成されたオブジェクト階層を返します。 data は、バイトのようなオブジェクトである必要があります。
ピクルスのプロトコルバージョンは自動的に検出されるため、プロトコル引数は必要ありません。 オブジェクトのpickle化された表現を超えたバイトは無視されます。
引数ファイル、 fix_imports 、エンコーディング、エラー、厳密およびバッファーには Unpickler コンストラクターと同じ意味です。
バージョン3.8で変更: buffers 引数が追加されました。
pickle モジュールは、次の3つの例外を定義します。
- exception pickle.PickleError
- 他の酸洗い例外の共通基本クラス。 例外を継承します。
- exception pickle.PicklingError
Pickler が選択できないオブジェクトを検出するとエラーが発生します。 PickleError を継承します。
酸洗いできるオブジェクトの種類については、酸洗いおよび酸洗い解除できるものを参照してください。
- exception pickle.UnpicklingError
データの破損やセキュリティ違反など、オブジェクトの選択解除に問題がある場合に発生するエラー。 PickleError を継承します。
AttributeError、EOFError、ImportError、IndexErrorなど、他の例外も選択解除中に発生する可能性があることに注意してください(必ずしもこれらに限定されません)。
pickle モジュールは、 Pickler 、 Unpickler 、 PickleBuffer の3つのクラスをエクスポートします。
- class pickle.Pickler(file, protocol=None, *, fix_imports=True, buffer_callback=None)
これは、pickleデータストリームを書き込むためのバイナリファイルを取ります。
オプションの protocol 引数(整数)は、指定されたプロトコルを使用するようにピッカーに指示します。 サポートされているプロトコルは0〜 HIGHEST_PROTOCOL です。 指定しない場合、デフォルトは DEFAULT_PROTOCOL です。 負の数を指定すると、 HIGHEST_PROTOCOL が選択されます。
file 引数には、1バイトの引数を受け入れるwrite()メソッドが必要です。 したがって、バイナリ書き込み用に開かれたディスク上のファイル、 io.BytesIO インスタンス、またはこのインターフェイスを満たすその他のカスタムオブジェクトにすることができます。
fix_imports がtrueで、 protocol が3未満の場合、pickleは新しいPython3名をPython2で使用されている古いモジュール名にマップしようとするため、pickleデータストリームは次のようになります。 Python2で読み取り可能。
buffer_callback がNone(デフォルト)の場合、バッファービューはピクルスストリームの一部としてファイルにシリアル化されます。
buffer_callback がNoneでない場合は、バッファービューを使用して何度でも呼び出すことができます。 コールバックがfalse値(Noneなど)を返す場合、指定されたバッファーは帯域外です。 それ以外の場合、バッファは帯域内でシリアル化されます。 ピクルスストリーム内。
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
ピッカーオブジェクトのディスパッチテーブルは、 copyreg.pickle()を使用して宣言できる種類の削減関数のレジストリです。 これは、キーがクラスで、値がリダクション関数であるマッピングです。 リダクション関数は、関連付けられたクラスの単一の引数を取り、
__reduce__()
メソッドと同じインターフェイスに準拠する必要があります。デフォルトでは、ピッカーオブジェクトには dispatch_table 属性がなく、代わりに copyreg モジュールによって管理されるグローバルディスパッチテーブルが使用されます。 ただし、特定のピッカーオブジェクトのピクルスをカスタマイズするには、 dispatch_table 属性をdictのようなオブジェクトに設定できます。 または、 Pickler のサブクラスに dispatch_table 属性がある場合、これはそのクラスのインスタンスのデフォルトのディスパッチテーブルとして使用されます。
使用例については、ディスパッチテーブルを参照してください。
バージョン3.3の新機能。
- reducer_override(self, obj)
Pickler サブクラスで定義できる特別なレデューサー。 このメソッドは、 dispatch_table のどのレデューサーよりも優先されます。
__reduce__()
メソッドと同じインターフェイスに準拠する必要があり、オプションでNotImplemented
を dispatch_table に登録されたレデューサーにフォールバックしてobj
をピクルスにすることができます。詳細な例については、タイプ、関数、およびその他のオブジェクトのカスタム削減を参照してください。
バージョン3.8の新機能。
- fast
非推奨。 真の値に設定されている場合は、高速モードを有効にします。 高速モードではメモの使用が無効になるため、余分なPUTオペコードが生成されないため、ピクルス処理が高速化されます。 自己参照オブジェクトと一緒に使用しないでください。使用すると、 Pickler が無限に再帰します。
よりコンパクトなピクルスが必要な場合は、 pickletools.optimize()を使用してください。
- class pickle.Unpickler(file, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)
これは、pickleデータストリームを読み取るためのバイナリファイルを取ります。
ピクルスのプロトコルバージョンは自動的に検出されるため、プロトコル引数は必要ありません。
引数 file には、 file のように、整数引数を取るread()メソッド、バッファー引数を取るreadinto()メソッド、引数を必要としないreadline()メソッドの3つのメソッドが必要です。 X211X] io.BufferedIOBase インターフェース。 したがって、 file は、バイナリ読み取り用に開かれたディスク上のファイル、 io.BytesIO オブジェクト、またはこのインターフェイスを満たすその他のカスタムオブジェクトにすることができます。
オプションの引数 fix_imports 、 encoding 、および errors は、Python2によって生成されたpickleストリームの互換性サポートを制御するために使用されます。 fix_imports がtrueの場合、pickleは古いPython2の名前をPython3で使用されている新しい名前にマップしようとします。 encoding および errors は、Python2によってpickle化された8ビット文字列インスタンスをデコードする方法をpickleに指示します。 これらのデフォルトは、それぞれ「ASCII」と「strict」です。 encoding は、これらの8ビット文字列インスタンスをbytesオブジェクトとして読み取るために「bytes」にすることができます。
encoding='latin1'
を使用することは、Python2によってpickle化された datetime 、 date 、および time のNumPy配列とインスタンスの選択を解除するために必要です。buffers がNone(デフォルト)の場合、逆シリアル化に必要なすべてのデータがピクルスストリームに含まれている必要があります。 これは、 Pickler がインスタンス化されたとき(または dump()または dumps()が呼び出されたとき)、 buffer_callback 引数がNoneであったことを意味します。 。
buffers がNoneでない場合、ピクルスストリームが帯域外バッファビューを参照するたびに消費されるバッファ対応オブジェクトの反復可能である必要があります。 このようなバッファは、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()は関数の検索にも使用されることに注意してください。
サブクラスはこれをオーバーライドして、オブジェクトのタイプとオブジェクトのロード方法を制御し、セキュリティリスクを軽減する可能性があります。 詳細については、グローバルの制限を参照してください。
- class pickle.PickleBuffer(buffer)
選択可能なデータを表すバッファーのラッパー。 buffer は、バイトのようなオブジェクトやN次元配列など、バッファ提供オブジェクトである必要があります。
PickleBuffer はそれ自体がバッファープロバイダーであるため、 memoryview などのバッファー提供オブジェクトを期待する他のAPIに渡すことができます。
PickleBuffer オブジェクトは、pickleプロトコル5以降を使用してのみシリアル化できます。 これらは、帯域外シリアル化の対象です。
バージョン3.8の新機能。
- raw()
このバッファの基礎となるメモリ領域の memoryview を返します。 返されるオブジェクトは、
B
(符号なしバイト)形式の1次元のC連続メモリビューです。 BufferError は、バッファーがCまたはFortranに隣接していない場合に発生します。
- release()
PickleBufferオブジェクトによって公開されている基になるバッファーを解放します。
何を漬けたり、漬けたりすることができますか?
以下のタイプを漬けることができます:
None
、True
、およびFalse
- 整数、浮動小数点数、複素数
- 文字列、バイト、バイト配列
- 選択可能なオブジェクトのみを含むタプル、リスト、セット、および辞書
- モジュールのトップレベルで定義された関数( lambda ではなく、 def を使用)
- モジュールのトップレベルで定義された組み込み関数
- モジュールのトップレベルで定義されているクラス
- __ dict __ または
__getstate__()
を呼び出した結果がピクル可能であるようなクラスのインスタンス(詳細については、セクションピクルスクラスインスタンスを参照)。
ピクルできないオブジェクトをピクルしようとすると、 PicklingError 例外が発生します。 これが発生した場合、不特定のバイト数が基になるファイルにすでに書き込まれている可能性があります。 再帰性の高いデータ構造をpickle化しようとすると、最大再帰深度を超える可能性があります。この場合、 RecursionError が発生します。 sys.setrecursionlimit()を使用して、この制限を慎重に引き上げることができます。
関数(組み込みおよびユーザー定義)は、値ではなく、「完全修飾」の名前参照によって選択されることに注意してください。 2 これは、関数が定義されているモジュールの名前とともに、関数名のみがpickle化されることを意味します。 関数のコードもその関数属性もpickle化されていません。 したがって、定義モジュールはピッキング解除環境にインポート可能である必要があり、モジュールには名前付きオブジェクトが含まれている必要があります。そうでない場合、例外が発生します。 3
同様に、クラスは名前付き参照によってピクルされるため、ピクル解除環境でも同じ制限が適用されます。 クラスのコードまたはデータはいずれもピクルされていないため、次の例では、クラス属性attr
はピクル解除環境では復元されないことに注意してください。
class Foo:
attr = 'A class attribute'
picklestring = pickle.dumps(Foo)
これらの制限により、選択可能な関数とクラスをモジュールのトップレベルで定義する必要があります。
同様に、クラスインスタンスがピクルされる場合、それらのクラスのコードとデータはそれらと一緒にピクルされません。 インスタンスデータのみがピクルされます。 これは意図的に行われるため、クラスのバグを修正したり、クラスにメソッドを追加したりしても、以前のバージョンのクラスで作成されたオブジェクトを読み込むことができます。 クラスの多くのバージョンを表示する長寿命のオブジェクトを計画している場合は、クラスの__setstate__()
メソッドで適切な変換を行えるように、オブジェクトにバージョン番号を付けることをお勧めします。
酸洗いクラスインスタンス
このセクションでは、クラスインスタンスのピクルスとアンピクルスの方法を定義、カスタマイズ、および制御するために使用できる一般的なメカニズムについて説明します。
ほとんどの場合、インスタンスを選択可能にするために追加のコードは必要ありません。 デフォルトでは、pickleはイントロスペクションを介してインスタンスのクラスと属性を取得します。 クラスインスタンスが選択解除されると、その__init__()
メソッドは通常呼び出されません。 デフォルトの動作では、最初に初期化されていないインスタンスが作成され、次に保存された属性が復元されます。 次のコードは、この動作の実装を示しています。
def save(obj):
return (obj.__class__, obj.__dict__)
def load(cls, attributes):
obj = cls.__new__(cls)
obj.__dict__.update(attributes)
return obj
クラスは、1つまたは複数の特別なメソッドを提供することにより、デフォルトの動作を変更できます。
- object.__getnewargs_ex__()
プロトコル2以降では、 __ getnewargs_ex __()メソッドを実装するクラスは、選択解除時に __ new __()メソッドに渡される値を指定できます。 このメソッドは、ペア
(args, kwargs)
を返す必要があります。ここで、 args は位置引数のタプルであり、 kwargs はオブジェクトを構築するための名前付き引数の辞書です。 これらは、ピックを解除すると __ new __()メソッドに渡されます。クラスの __ new __()メソッドでキーワードのみの引数が必要な場合は、このメソッドを実装する必要があります。 それ以外の場合は、互換性のために __ getnewargs __()を実装することをお勧めします。
バージョン3.6で変更: __ getnewargs_ex __()がプロトコル2および3で使用されるようになりました。
- object.__getnewargs__()
このメソッドは、 __ getnewargs_ex __()と同様の目的を果たしますが、位置引数のみをサポートします。 引数のタプル
args
を返す必要があります。これは、選択を解除すると __ new __()メソッドに渡されます。__ getnewargs_ex __()が定義されている場合、 __ getnewargs __()は呼び出されません。
バージョン3.6での変更: Python 3.6より前は、プロトコル2および3で __ getnewargs_ex __()の代わりに __ getnewargs __()が呼び出されていました。
- object.__getstate__()
- クラスは、インスタンスのピクル化方法にさらに影響を与える可能性があります。 クラスがメソッド __ getstate __()を定義している場合、そのクラスが呼び出され、返されたオブジェクトが、インスタンスのディクショナリのコンテンツではなく、インスタンスのコンテンツとして選択されます。 __ getstate __()メソッドがない場合、インスタンスの __ dict __ は通常どおりpickle化されます。
- object.__setstate__(state)
選択解除時に、クラスが __ setstate __()を定義している場合、選択解除された状態で呼び出されます。 その場合、状態オブジェクトがディクショナリである必要はありません。 それ以外の場合、pickle化された状態はディクショナリである必要があり、そのアイテムは新しいインスタンスのディクショナリに割り当てられます。
ノート
__ getstate __()がfalse値を返した場合、 __ setstate __()メソッドは選択解除時に呼び出されません。
メソッド__getstate__()
および__setstate__()
の使用方法の詳細については、セクションステートフルオブジェクトの処理を参照してください。
ノート
選択解除時に、__getattr__()
、__getattribute__()
、または__setattr__()
などの一部のメソッドがインスタンスで呼び出される場合があります。 これらのメソッドが内部不変条件がtrueであることに依存している場合、インスタンスの選択を解除するときに__init__()
が呼び出されないため、型は__new__()
を実装してそのような不変条件を確立する必要があります。
これから説明するように、ピクルスは上記の方法を直接使用しません。 実際、これらのメソッドは、__reduce__()
特殊メソッドを実装するコピープロトコルの一部です。 コピープロトコルは、オブジェクトのピクルス化とコピーに必要なデータを取得するための統一されたインターフェイスを提供します。 4
強力ですが、__reduce__()
をクラスに直接実装するとエラーが発生しやすくなります。 このため、クラス設計者は可能な限り高レベルのインターフェイス(__getnewargs_ex__()
、__getstate__()
、__setstate__()
)を使用する必要があります。 ただし、__reduce__()
を使用することが唯一のオプションである場合、またはより効率的な酸洗いにつながる場合、あるいはその両方の場合を示します。
- object.__reduce__()
現在、インターフェースは次のように定義されています。 __ reduce __()メソッドは引数をとらず、文字列またはできればタプルを返します(返されたオブジェクトは「reducevalue」と呼ばれることがよくあります)。
文字列が返される場合、その文字列はグローバル変数の名前として解釈される必要があります。 モジュールに関連するオブジェクトのローカル名である必要があります。 pickleモジュールは、モジュールの名前空間を検索して、オブジェクトのモジュールを判別します。 この動作は通常、シングルトンに役立ちます。
タプルが返されるとき、それは2から6アイテムの長さでなければなりません。 オプション項目は省略可能、または
None
を値として指定することができます。 各項目のセマンティクスは次のとおりです。オブジェクトの初期バージョンを作成するために呼び出される呼び出し可能なオブジェクト。
呼び出し可能オブジェクトの引数のタプル。 呼び出し可能オブジェクトが引数を受け入れない場合は、空のタプルを指定する必要があります。
オプションで、オブジェクトの状態。前述のように、オブジェクトの __ setstate __()メソッドに渡されます。 オブジェクトにそのようなメソッドがない場合、値はディクショナリである必要があり、オブジェクトの __ dict __ 属性に追加されます。
オプションで、連続するアイテムを生成するイテレータ(シーケンスではない)。 これらのアイテムは、
obj.append(item)
を使用するか、バッチでobj.extend(list_of_items)
を使用してオブジェクトに追加されます。 これは主にリストサブクラスに使用されますが、適切な署名を持つappend()
およびextend()
メソッドがある限り、他のクラスでも使用できます。 (append()
またはextend()
のどちらを使用するかは、使用するピクルスプロトコルのバージョンと追加するアイテムの数によって異なるため、両方をサポートする必要があります。)オプションで、連続するキーと値のペアを生成するイテレータ(シーケンスではない)。 これらのアイテムは、
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値を提供することです。
外部オブジェクトの永続性
オブジェクトの永続性を確保するために、 pickle モジュールは、pickle化されたデータストリームの外部にあるオブジェクトへの参照の概念をサポートしています。 このようなオブジェクトは、英数字の文字列(プロトコル0の場合) 5 または任意のオブジェクト(新しいプロトコルの場合)のいずれかである永続IDによって参照されます。
このような永続IDの解決は、 pickle モジュールでは定義されていません。 この解決策は、ピクラーとアンピクラーのユーザー定義メソッド persistent_id()と persistent_load()にそれぞれ委任されます。
外部永続IDを持つオブジェクトをピクルするには、ピッカーは、オブジェクトを引数として受け取り、None
またはそのオブジェクトの永続IDのいずれかを返すカスタム persistent_id()メソッドを持っている必要があります。 None
が返されると、ピッカーは通常どおりオブジェクトをピクルスします。 永続的なID文字列が返されると、ピッカーはマーカーとともにそのオブジェクトをピクルスにし、ピクル解除者がそれを永続的なIDとして認識するようにします。
外部オブジェクトをアンピックするには、アンピッカーに、永続IDオブジェクトを取得して参照オブジェクトを返すカスタム persistent_load()メソッドが必要です。
これは、永続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()
ディスパッチテーブル
ピクルスに依存する他のコードを邪魔することなく、いくつかのクラスのピクルスをカスタマイズしたい場合は、プライベートディスパッチテーブルを使用してピッカーを作成できます。
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)
ステートフルオブジェクトの処理
これは、クラスのピクルス動作を変更する方法を示す例です。 TextReader
クラスはテキストファイルを開き、readline()
メソッドが呼び出されるたびに行番号と行の内容を返します。 TextReader
インスタンスが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 の柔軟性が十分でない場合があります。 特に、オブジェクトのタイプ以外の基準に基づいてピクルスをカスタマイズしたり、関数やクラスのピクルスをカスタマイズしたりすることができます。
このような場合、 Pickler クラスからサブクラス化し、 reducer_override()メソッドを実装することができます。 このメソッドは、任意のリダクションタプルを返すことができます(__reduce__()
を参照)。 または、NotImplemented
を返して、従来の動作にフォールバックすることもできます。
dispatch_table と reducer_override()の両方が定義されている場合、 reducer_override()メソッドが優先されます。
ノート
パフォーマンス上の理由から、 reducer_override()は、None
、True
、False
、およびの正確なインスタンスに対して呼び出されない場合があります。 int 、 float 、 bytes 、 str 、 dict 、 set 、 floatset [ X257X]、リストおよびタプル。
これは、特定のクラスのピクルス化と再構築を許可する簡単な例です。
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 モジュールの通常の操作では、オブジェクトのグラフのような構造がバイトのシーケンシャルストリームに変換されるため、本質的に、ピクルストリームとの間でデータをコピーする必要があります。
プロバイダー(転送されるオブジェクトタイプの実装)とコンシューマー(通信システムの実装)の両方が帯域外をサポートしている場合、この制約を回避できます。ピクルスプロトコル5以降で提供される転送機能。
プロバイダーAPI
ピクルス化されるラージデータオブジェクトは、プロトコル5以降に特化した__reduce_ex__()
メソッドを実装する必要があります。このメソッドは、 PickleBuffer インスタンスを返します(例の代わりに) 大きなデータの場合は bytes オブジェクト)。
PickleBuffer オブジェクトは、基になるバッファーが帯域外データ転送に適格であることを通知します。 これらのオブジェクトは、 pickle モジュールの通常の使用法との互換性を維持します。 ただし、消費者はオプトインして、 pickle にそれらのバッファーを自分で処理することを伝えることもできます。
コンシューマーAPI
通信システムは、オブジェクトグラフをシリアル化するときに生成される PickleBuffer オブジェクトのカスタム処理を有効にすることができます。
送信側では、 buffer_callback 引数を Pickler (または dump()または dumps()関数に渡す必要があります)。これは、オブジェクトグラフのピクルス処理中に生成された各 PickleBuffer で呼び出されます。 buffer_callback によって蓄積されたバッファーは、データがピクルスストリームにコピーされたのを認識せず、安価なマーカーのみが挿入されます。
受信側では、 buffers 引数を Unpickler (または load()または load()関数に渡す必要があります)、これは buffer_callback に渡されたバッファーの反復可能です。 その反復可能オブジェクトは、 buffer_callback に渡されたのと同じ順序でバッファーを生成する必要があります。 これらのバッファは、ピクルスが元の PickleBuffer オブジェクトを生成したオブジェクトの再構築者が期待するデータを提供します。
送信側と受信側の間で、通信システムは帯域外バッファ用の独自の転送メカニズムを自由に実装できます。 潜在的な最適化には、共有メモリまたはデータ型に依存する圧縮の使用が含まれます。
例
これは、帯域外バッファピクルスに参加できる 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
クラスメソッド)は、適切なタイプの場合、バッファーの提供オブジェクトを返します。 これは、このおもちゃの例でゼロコピーの動作をシミュレートする簡単な方法です。
コンシューマー側では、通常の方法でこれらのオブジェクトをピクルス化できます。シリアル化されていない場合、元のオブジェクトのコピーが得られます。
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化を解除すると、pickleデータで見つかったクラスまたは関数がインポートされます。 多くのアプリケーションでは、この動作は、ピクラーが任意のコードをインポートして呼び出すことを許可するため、受け入れられません。 この手作りのピクルスデータストリームがロードされたときに何をするかを考えてみてください。
>>> import pickle
>>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
hello world
0
この例では、unpicklerは os.system()関数をインポートしてから、文字列引数「echohelloworld」を適用します。 この例は不快ではありませんが、システムに損傷を与える可能性のある例を想像するのは難しくありません。
このため、 Unpickler.find_class()をカスタマイズして、何が選択解除されるかを制御することをお勧めします。 その名前が示すように、 Unpickler.find_class()は、グローバル(つまり、クラスまたは関数)が要求されるたびに呼び出されます。 したがって、グローバルを完全に禁止するか、グローバルを安全なサブセットに制限することができます。
これは、 builtins モジュールからのいくつかの安全なクラスのみをロードできるようにするアンピッカーの例です。
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()
私たちのピクラー解除作業の使用例は、次のことを意図しています。
>>> 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
私たちの例が示すように、あなたはあなたが摘み取られることを許すものに注意しなければなりません。 したがって、セキュリティが懸念される場合は、 xmlrpc.client のマーシャリング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)
も参照してください
- モジュール copyreg
- 拡張タイプのPickleインターフェースコンストラクター登録。
- モジュール pickletools
- ピクルス化されたデータを操作および分析するためのツール。
- モジュールシェルフ
- オブジェクトのインデックス付きデータベース。 picle を使用します。
- モジュールコピー
- 浅くて深いオブジェクトのコピー。
- モジュールマーシャル
- 組み込み型の高性能シリアル化。
脚注
- 1
- これをマーシャルモジュールと混同しないでください
- 2
- これが、 lambda 関数をpickle化できない理由です。すべての
lambda
関数は同じ名前<lambda>
を共有します。 - 3
- 発生する例外は、 ImportError または AttributeError である可能性がありますが、それ以外の可能性があります。
- 4
- copy モジュールは、浅いコピー操作と深いコピー操作にこのプロトコルを使用します。
- 5
- 英数字の制限は、プロトコル0の永続IDが改行文字で区切られているためです。 したがって、永続IDに何らかの改行文字が含まれていると、結果のピクルスが読み取れなくなります。