pickle --- Python オブジェクトの直列化

ソースコード: Lib/pickle.py


pickle モジュールは、Python オブジェクト構造を直列化・復元するためのバイナリプロトコルを実装しています。 pickle 化 (pickling) とは Python オブジェクトの階層をバイトストリームに変換する処理です。 非 pickle 化 (unpickling) はその逆で、バイナリファイル や bytes-like object からのバイトストリームをオブジェクトの階層に戻します。 pickle 化と非 pickle 化は、直列化 (serialization)、整列化 (marshalling) [1]、平坦化 (flattening) とも呼ばれますが、ここでは混乱を避けるため「pickle 化」「非 pickle 化」という用語を使います。

警告

pickle モジュールは 安全ではありません 。信頼できるデータのみを非 pickle 化してください。

非 pickle 化の過程で任意のコードを実行する ような、悪意ある pickle オブジェクトを生成することが可能です。信頼できない提供元からのデータや、改竄された可能性のあるデータの非 pickle 化は絶対に行わないでください。

データが改竄されていないことを保証したい場合は、 hmac による鍵付きハッシュ化を検討してください。

信頼できないデータを処理する場合 json のようなより安全な直列化形式の方が適切でしょう。 json との比較 を参照してください。

他の Python モジュールとの関係

marshal との比較

Python には marshal という、より基本的な直列化モジュールもありますが、一般に Python オブジェクトの直列化には常に pickle を優先して使ってください。 marshal は主に Python の .pyc ファイルをサポートするためのものです。

pickle モジュールは、いくつかの重要な点で marshal と異なります:

  • marshal はユーザー定義クラスやそのインスタンスを直列化できません。 pickle はクラスのインスタンスを透過的に保存・復元できますが、クラス定義をインポートできる必要があり、オブジェクトを pickle 化したときと同じモジュールに定義がなければなりません。

  • marshal の直列化形式は、Python のバージョン間で移植できる保証がありません。 主な用途が .pyc ファイルのサポートであるため、Python の実装者は必要に応じて、後方互換性のない形で直列化形式を変更することがあります。 一方、pickle の直列化形式は、互換性のある 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 など、外部の標準による制約を受けない利点があります。 一方、Python 以外のプログラムでは、pickle 化された Python オブジェクトを再構成できない場合があります。

pickle のデータ形式は、デフォルトで比較的コンパクトなバイナリ表現を使います。 サイズをさらに小さくする必要がある場合、pickle 化されたデータは効率よく 圧縮 できます。

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から3.13 までのデフォルトプロトコルでした。プロトコル 4 による改良に関する情報は PEP 3154 を参照してください。

  • プロトコルバージョン 5 は Python 3.8 で追加されました。このバージョンでは帯域外データのサポートが追加され、また帯域内データに対するパフォーマンスが向上します。これはPython 3.14からのデフォルトプロトコルです。プロトコルバージョン 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 より小さい場合があります。 現在のデフォルトはプロトコル 5 で、Python 3.8 で導入され、それより前のバージョンとは互換性がありません。 このバージョンでは帯域外バッファをサポートし、PEP 3118 に準拠したデータをメインの pickle ストリームとは別に転送できます。

バージョン 3.0 で変更: デフォルトプロトコルは 3 です。

バージョン 3.8 で変更: デフォルトプロトコルは 4 です。

バージョン 3.14 で変更: デフォルトプロトコルは 5 です。

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 化オブジェクト表現より後のバイト列は無視されます。

引数 fix_imports, encoding, errors, strict および buffers は Unpickler のコンストラクタと同じ意味になります。

バージョン 3.8 で変更: buffers 引数が追加されました。

pickle モジュールは 3 つの例外を定義しています:

exception pickle.PickleError

他の pickle 化例外の共通基底クラス。Exception を継承しています。

exception pickle.PicklingError

Pickler が pickle 化不可能なオブジェクトに遭遇したときに送出されるエラー。PickleError を継承しています。

どんな種類のオブジェクトが pickle 化できるのか確認するには pickle 化、非 pickle 化できるもの を参照してください。

exception pickle.UnpicklingError

データ破損やセキュリティ違反のような、オブジェクトを非 pickle 化するのに問題がある場合に送出されるエラー。PickleError を継承します。

非 picke 化の最中に他の例外が送出されることもあるので注意してください。これには AttributeError, EOFError, ImportError, IndexError が含まれます (ただし必ずしもこれらに限定されません)。

pickle モジュールは Pickler、Unpickler、PickleBuffer の 3 つのクラスを公開しています:

class pickle.Pickler(file, protocol=None, *, fix_imports=True, buffer_callback=None)

pickle 化されたオブジェクトのデータストリームを書き込むためのバイナリファイルを引数にとります。

任意の引数 protocol は、整数で、pickle 化で使用するプロトコルを指定します; サポートされているプロトコルは 0 から HIGHEST_PROTOCOL までになります。指定されない場合、DEFAULT_PROTOCOL が使用されます。負数が与えられた場合、HIGHEST_PROTOCOL が使用されます。

引数 file は、単一の bytes 引数を受け付ける write() メソッドを持たなければなりません。 したがって、バイナリ書き込み用にオープンされたディスク上のファイル、io.BytesIO インスタンス、またはこのインターフェースに適合するその他のカスタムオブジェクトを指定できます。

fix_imports が真であり、かつ、protocol が 3 未満の場合、pickle は新しい Python 3 の名前と Python 2 で使用されていた古いモジュール名との対応付けを試みるので、pickle データストリームは Python 2 でも読み込み可能です。

buffer_callback が None (デフォルト) の場合、バッファビューはストリームの一部として*file* 中に直列化されます。

buffer_callback が None でない場合、バッファビューを引数として何度でも呼び出されることがあります。 コールバックが None などの偽の値を返すと、渡されたバッファは 帯域外 で扱われます。 それ以外の場合は帯域内、つまり 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 を持つことができないことに注意してください。

詳細および使用例については 外部オブジェクトの永続化 を参照してください。

バージョン 3.13 で変更: Pickler の C 実装に、このメソッドのデフォルト実装を追加しました。

dispatch_table

pickler オブジェクトのディスパッチテーブルは、copyreg.pickle() で宣言できる種類の リダクション関数 を登録するものです。 これはクラスをキー、リダクション関数を値とするマッピングです。 リダクション関数は、対応するクラスのインスタンスを 1 つ引数に取り、__reduce__() メソッドと同じインターフェースに従う必要があります。

デフォルトでは、pickler オブジェクトは dispatch_table 属性を持たず、代わりに copyreg モジュールによって管理されるグローバルなディスパッチテーブルを使用します。しかし、特定の pickler オブジェクトによる pickle 化をカスタマイズするために dispatch_table 属性に dict-like オブジェクトを設定することができます。あるいは、Pickler のサブクラスが dispatch_table 属性を持てば、そのクラスのインスタンスに対するデフォルトのディスパッチテーブルとして使用されます。

使用例については ディスパッチテーブル を参照してください。

Added in version 3.3.

reducer_override(obj)

Pickler のサブクラスで定義可能な特殊なリデューサ (reducer) です。このメソッドは dispatch_table 内のいかなるリデューサよりも優先されます。このメソッドは __reduce__() メソッドのインターフェースと適合していなければなりません。また、メソッドが NotImplemented を返すことにより、 dispatch_table に登録されたリデューサにフォールバックして obj を直列化することもできます。

詳細な例については、 型、関数、その他のオブジェクトに対するリダクションのカスタマイズ を参照してください。

Added in version 3.8.

fast

廃止予定です。真値が設定されれば高速モードを有効にします。高速モードは、メモの使用を無効にします。それにより余分な PUT 命令コードを生成しなくなるので pickle 化処理が高速化します。自己参照オブジェクトに対しては使用すべきではありません。さもなければ Pickler に無限再帰を起こさせるでしょう。

よりコンパクトな pickle 化を必要とする場合は、pickletools.optimize() を使用してください。

clear_memo()

pickler の「メモ」を消去します。

メモは、共有されているオブジェクトや再帰的なオブジェクトを値ではなく参照として pickle 化するために、pickler がすでに処理したオブジェクトを記憶するデータ構造です。 このメソッドは pickler を再利用するときに便利です。

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 でない場合は、バッファを提供するオブジェクトのイテラブルでなければなりません。 pickle ストリームが 帯域外 (out-of-band) のバッファビューを参照するたびに、このイテラブルからバッファを 1 つ取得します。 これらのバッファは、Pickler オブジェクトの buffer_callback に渡された順に並べます。

バージョン 3.8 で変更: buffers 引数が追加されました。

load()

コンストラクターで与えられたオープンしたファイルオブジェクトからオブジェクトの pickle 化表現を読み込み、その中で指定されたオブジェクト階層に再構成して返します。オブジェクトの pickle 化表現より後のバイト列は無視されます。

persistent_load(pid)

デフォルトで UnpicklingError を送出します。

もし定義されていれば、persistent_load() は永続的な ID pid によって指定されたオブジェクトを返す必要があります。永続的な ID が無効な場合、UnpicklingError を送出しなければなりません。

詳細および使用例については 外部オブジェクトの永続化 を参照してください。

バージョン 3.13 で変更: Unpickler の C 実装に、このメソッドのデフォルト実装を追加しました。

find_class(module, name)

必要なら module をインポートして、そこから name という名前のオブジェクトを返します。ここで module および name 引数は str オブジェクトです。その名前が示唆することに反して find_class() は関数を探すためにも使われることに注意してください。

サブクラスは、どんな型のオブジェクトを、どのようにロードするか (潜在的にはセキュリティリスクの減少) に関する制御を得るためにこれをオーバーライドすることができます。詳細に関しては グローバル変数を制限する を参照してください。

引数 module, name を指定して 監査イベント pickle.find_class を送出します。

class pickle.PickleBuffer(buffer)

pickle 化できるデータを表すバッファのラッパーです。 buffer は bytes-like object や N 次元配列のような、バッファを提供する オブジェクトでなければなりません。

PickleBuffer はそれ自身バッファ機能を提供します。したがってこのクラスのインスタンスを、バッファ機能を提供するオブジェクトを期待する memoryview など他の API に渡すことが可能です。

PickleBuffer オブジェクトは pickle プロトコル 5 以降でのみ直列化できます。 帯域外の直列化 に対応しています。

Added in version 3.8.

raw()

このバッファの背後にあるメモリ領域への memoryview を返します。戻り値のオブジェクトはフォーマット B (符号なしバイト) の C-連続な1次元のメモリビューです。バッファが C-連続でも Fortran-連続でもない場合 BufferError 例外が送出されます。

release()

PickleBuffer オブジェクトを通じてアクセスされる背後のバッファを解放します。

pickle 化、非 pickle 化できるもの

以下の型は pickle 化できます:

  • 組み込み定数(None, True, False, Ellipsis, と NotImplemented)

  • 整数、浮動小数点数、複素数

  • 文字列、バイト列、バイト配列

  • pickle 化可能なオブジェクトからなるタプル、リスト、集合および辞書

  • モジュールのトップレベルからアクセスできる関数 (組み込み関数とユーザー定義関数。 lambda ではなく def で定義したもの)

  • モジュールのトップレベルからアクセスできるクラス

  • 上記のクラスのインスタンスのうち、__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 restore(cls, attributes):
    obj = cls.__new__(cls)
    obj.__dict__.update(attributes)
    return obj

クラスは、いくつかの特殊メソッドを提供することによって、デフォルトの振る舞いを変更することができます:

object.__getnewargs_ex__()

プロトコル 2 以降では、__getnewargs_ex__() メソッドを実装したクラスは、非 pickle 化時に __new__() メソッドへ渡す値を指定できます。 このメソッドは (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__()

__getstate__() メソッドをオーバーライドすることで、クラスはインスタンスの pickle 化の動作をさらに変更できます。 このメソッドを呼び出して返されたオブジェクトが、デフォルトの状態の代わりにインスタンスの内容として pickle 化されます。 デフォルトの状態には次のケースがあります:

  • インスタンスの __dict__ も __slots__ もないクラスでは、デフォルトの状態は None です。

  • インスタンスの __dict__ があり、__slots__ がないクラスでは、デフォルトの状態は self.__dict__ です。

  • インスタンスの __dict__ と __slots__ の両方があるクラスでは、デフォルトの状態は 2 つの辞書からなるタプルです。 1 つ目は self.__dict__、2 つ目はスロット名をスロットの値に対応付ける辞書です。 後者には値のあるスロットだけが含まれます。

  • __slots__ があり、インスタンスの __dict__ がないクラスでは、デフォルトの状態はタプルで、その最初の要素は None、2 番目の要素は前の項目で説明した、スロット名をスロットの値に対応付ける辞書です。

バージョン 3.11 で変更: object クラスに __getstate__() メソッドのデフォルト実装を追加しました。

object.__setstate__(state)

非 pickle 化に際して、クラスが __setstate__() を定義している場合、それは非 pickle 化された状態とともに呼び出されます。その場合、状態オブジェクトが辞書でなければならないという要求はありません。そうでなければ、 pickle された状態は辞書で、その要素は新しいインスタンスの辞書に割り当てられます。

注釈

pickle 化時に __reduce__() が返す状態の値が None の場合、非 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" と呼ばれます)。

If a string is returned, the string should be interpreted as the name of a global variable. It should be the object's local name relative to its module; the pickle module searches the module namespace to determine the object's module: for a given obj to be pickled, the __module__ attribute is looked up on obj directly, which falls back to a lookup on the type of obj if no __module__ instance attribute is set. This behaviour is typically useful for singletons.

タプルが返された場合、それは 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__() メソッドに優先します。

    Added in version 3.8: 任意の6番目のタプル要素 (obj, state) が追加されました。

object.__reduce_ex__(protocol)

別の方法として、__reduce_ex__() メソッドを定義することもできます。唯一の違いは、このメソッドは単一の整数引数、プロトコルバージョンを取る必要があるということです。もし定義された場合、pickle は __reduce__() メソッドよりもこのメソッドを優先します。さらに、__reduce__() は自動的に拡張版の同義語になります。このメソッドの主な用途は、古い Python リリースに対して後方互換性のある reduce value を提供することです。

外部オブジェクトの永続化

オブジェクトの永続化を支援するため、pickle モジュールは pickle データストリームの外にあるオブジェクトへの参照をサポートしています。 こうしたオブジェクトは永続的 ID で参照します。 永続的 ID は、プロトコル 0 では英数字の文字列 [5]、それより新しいプロトコルでは任意のオブジェクトでなければなりません。

このような永続的 ID の解決方法は pickle モジュールでは定義していません。 解決処理は pickler と unpickler のユーザー定義メソッド、それぞれ persistent_id() と persistent_load() に委譲します。

外部の永続的 ID を持つオブジェクトを pickle 化するには、pickler は、オブジェクトを引数として取り、None またはそのオブジェクトの永続的 ID を返す、カスタムの persistent_id() メソッドを持たなければなりません。 None が返された場合、pickler はそのオブジェクトを通常どおり pickle 化します。 永続的 ID の文字列が返された場合、pickler は、unpickler がそれを永続的 ID として認識できるように、マーカーとともにその ID を pickle 化します。

外部オブジェクトを非 pickle 化するには、unpickler は永続的 ID オブジェクトを取り被参照オブジェクトを返すカスタム persistent_load() メソッドを持たなくてはなりません。

これは、外部のオブジェクトを参照によって pickle 化するために永続的 ID をどのように使用するかを示す包括的な例です。

# 永続的 ID を使い、外部のオブジェクトを参照として pickle 化する簡単な例。

import pickle
import sqlite3
from collections import namedtuple

# データベース内のレコードを表す簡単なクラス。
MemoRecord = namedtuple("MemoRecord", "key, task")

class DBPickler(pickle.Pickler):

    def persistent_id(self, obj):
        # MemoRecord を通常のクラスインスタンスとして pickle 化する代わりに、
        # 永続的 ID を出力する。
        if isinstance(obj, MemoRecord):
            # ここでの永続的 ID は、タグとデータベース内の特定のレコードを指すキーを
            # 含むタプルにすぎない。
            return ("MemoRecord", obj.key)
        else:
            # obj に永続的 ID がなければ None を返す。
            # これは obj を通常どおり pickle 化する必要があることを示す。
            return None


class DBUnpickler(pickle.Unpickler):

    def __init__(self, file, connection):
        super().__init__(file)
        self.connection = connection

    def persistent_load(self, pid):
        # このメソッドは永続的 ID が現れるたびに呼ばれる。
        # ここで pid は DBPickler が返したタプル。
        cursor = self.connection.cursor()
        type_tag, key_id = pid
        if type_tag == "MemoRecord":
            # 参照されたレコードをデータベースから取得して返す。
            cursor.execute("SELECT * FROM memos WHERE key=?", (str(key_id),))
            key, task = cursor.fetchone()
            return MemoRecord(key, task)
        else:
            # 正しいオブジェクトを返せない場合は必ずエラーを送出する。
            # そうしないと、unpickler は永続的 ID が参照するオブジェクトが
            # None だとみなしてしまう。
            raise pickle.UnpicklingError("unsupported persistent object")


def main():
    import io
    import pprint

    # データベースを初期化し、データを挿入する。
    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,))

    # pickle 化するレコードを取得する。
    cursor.execute("SELECT * FROM memos")
    memos = [MemoRecord(key, task) for key, task in cursor]
    # 独自の DBPickler を使ってレコードを保存する。
    file = io.BytesIO()
    DBPickler(file).dump(memos)

    print("Pickled records:")
    pprint.pprint(memos)

    # 試しにレコードを更新する。
    cursor.execute("UPDATE memos SET task='learn italian' WHERE key=1")

    # pickle データストリームからレコードを読み込む。
    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.pickle(SomeClass, reduce_SomeClass)
f = io.BytesIO()
p = pickle.Pickler(f)

copyreg モジュールのすべての利用者が共有するグローバルなディスパッチテーブルを変更します。

状態を持つオブジェクトの扱い

クラスの pickle 化の動作を変更する例を示します。 以下の TextReader クラスはテキストファイルを開き、readline() メソッドが呼ばれるたびに行番号と行の内容を返します。 TextReader インスタンスを pickle 化すると、ファイルオブジェクトのメンバーを 除く すべての属性を保存します。 非 pickle 化するとファイルを開き直し、最後の位置から読み取りを再開します。 この動作は __setstate__() と __getstate__() メソッドで実装しています。

class TextReader:
    """テキストファイルの行に番号を付けて表示する。"""

    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):
        # すべてのインスタンス属性を含む self.__dict__ からオブジェクトの状態をコピーする。
        # 元の状態を変更しないよう、必ず dict.copy() メソッドを使う。
        state = self.__dict__.copy()
        # pickle 化できない要素を取り除く。
        del state['file']
        return state

    def __setstate__(self, state):
        # インスタンス属性 (filename と lineno) を復元する。
        self.__dict__.update(state)
        # 以前開いていたファイルの状態を復元する。
        # そのためにファイルを開き直し、行数が元に戻るまで読み進める。
        file = open(self.filename)
        for _ in range(self.lineno):
            file.readline()
        # 最後にファイルを保存する。
        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!'

型、関数、その他のオブジェクトに対するリダクションのカスタマイズ

Added in version 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):
        """MyClass 用の独自のリデューサ。"""
        if getattr(obj, "__name__", None) == "MyClass":
            return type, (obj.__name__, obj.__bases__,
                          {'my_attribute': obj.my_attribute})
        else:
            # 他のオブジェクトでは通常のリダクションにフォールバックする
            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

帯域外バッファ

Added in version 3.8.

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:
            # pickle プロトコル 4 以下では PickleBuffer を使えない。
            return type(self)._reconstruct, (bytearray(self),)

    @classmethod
    def _reconstruct(cls, obj):
        with memoryview(obj) as m:
            # 元のバッファオブジェクトへの参照を取得する
            obj = m.obj
            if type(obj) is cls:
                # 元のバッファオブジェクトが ZeroCopyByteArray なので、そのまま返す。
                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: コピーが作られた

いっぽう直列化において 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: コピーは作られなかった

この例では 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):
        # builtins 内の安全なクラスだけを許可する。
        if module == "builtins" and name in safe_builtins:
            return getattr(builtins, name)
        # それ以外はすべて禁止する。
        raise pickle.UnpicklingError("global '%s.%s' is forbidden" %
                                     (module, name))

def restricted_loads(s):
    """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

# pickle がサポートするオブジェクトの任意の集まり。
data = {
    'a': [1, 2.0, 3+4j],
    'b': ("character string", b"byte string"),
    'c': {None, True, False}
}

with open('data.pickle', 'wb') as f:
    # 利用できる最大のプロトコルバージョンで 'data' 辞書を pickle 化する。
    pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)

次の例は、pickle 化されたデータを読み込みます。

import pickle

with open('data.pickle', 'rb') as f:
    # プロトコルバージョンは自動的に検出されるので、指定する必要はない。
    data = pickle.load(f)

コマンドライン・インターフェース

pickle モジュールはコマンドラインからスクリプトとして呼び出すことができ、pickle ファイルの内容を表示します。 ただし、調べたい pickle ファイルが信頼できない情報源からのものである場合は、pickle バイトコードを実行しない -m pickletools の方が安全です。 pickletools のコマンドラインでの使用 を参照してください。

python -m pickle pickle_file [pickle_file ...]

次のオプションを受け付けます:

pickle_file

読み込む pickle ファイル。 標準入力から読み込む場合は - を指定します。

参考

copyreg モジュール

拡張型を登録するための Pickle インターフェース構成機構。

pickletools モジュール

pickle データの処理や分析を行うためのツール。

shelve モジュール

索引付きのオブジェクトのデータベース。 pickle を使います。

copy モジュール

オブジェクトの浅いコピーおよび深いコピー。

marshal モジュール

組み込み型の高性能な直列化。

脚注