12.1. "pickle" --- Python オブジェクトの直列化
**********************************************

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

======================================================================

"pickle" モジュールは Python オブジェクトの直列化および直列化されたオ
ブジェクトの復元のためのバイナリプロトコルを実装しています。*"Pickle
化"* は Python オブジェクト階層をバイトストリームに変換する処理、*"非
pickle 化"* は (*バイナリファイル* または *バイトライクオブジェクト*
から) バイトストリームをオブジェクト階層に復元する処理を意味します。
pickle 化 (および非 pickle 化) は "直列化 (serialization)"、"整列化
(marshalling)"、あるいは [1] "平坦化 (flattening)" とも呼ばれますが、
混乱を避けるため、ここでは "Pickle 化"、"非 pickle 化" で統一します。

警告: "pickle" モジュールはエラーや不正に生成されたデータに対して安
  全では ありません。信頼できない、あるいは認証されていないソースから
  受け取っ たデータを非 pickle 化してはいけません。


12.1.1. 他の Python モジュールとの関係
======================================


12.1.1.1. "marshal" との比較
----------------------------

Python には "marshal" と呼ばれるより原始的な直列化モジュールがあります
が、一般的に Python オブジェクトを直列化する方法としては "pickle" を選
ぶべきです。 "marshal" は基本的に ".pyc" ファイルをサポートするために
存在しています。

"pickle" モジュールはいくつかの点で "marshal" と明確に異なります:

* "pickle" モジュールでは、同じオブジェクトが再度直列化されることの
  な いよう、すでに直列化されたオブジェクトについて追跡情報を保持しま
  す。 "marshal" はこれを行いません。

  この機能は再帰的オブジェクトと共有オブジェクトの両方に重要な関わりを
  もっています。再帰的オブジェクトとは自分自身に対する参照を持っている
  オブジェクトです。再帰的オブジェクトは marshal で扱うことができず、
  実際、再帰的オブジェクトを marshal 化しようとすると Python インタプ
  リタをクラッシュさせてしまいます。共有オブジェクトは、直列化しようと
  するオブジェクト階層の異なる複数の場所で同じオブジェクトに対する参照
  が存在する場合に生じます。共有オブジェクトを共有のままにしておくこと
  は、変更可能なオブジェクトの場合には非常に重要です。

* "marshal" はユーザ定義クラスやそのインスタンスを直列化するために使
  う ことができません。 "pickle" はクラスインスタンスを透過的に保存し
  たり 復元したりすることができますが、クラス定義をインポートすること
  が可能 で、かつオブジェクトが保存された際と同じモジュールで定義され
  ていなけ ればなりません。

* "marshal" の直列化フォーマットは Python の異なるバージョンで可搬性
  が あることを保証していません。 "marshal" の本来の仕事は ".pyc" ファ
  イ ルのサポートなので、Python を実装する人々には、必要に応じて直列化
  フ ォーマットを以前のバージョンと互換性のないものに変更する権限が残
  され ています。 "pickle" 直列化フォーマットには、全ての Python リリ
  ース間 で以前のバージョンとの互換性が保証されています。


12.1.1.2. "json" との比較
-------------------------

pickle プロトコルと JSON (JavaScript Object Notation) との基本的な違い
は以下のとおりです:

* JSON はテキストの直列化フォーマット (大抵の場合 "utf-8" にエンコー
  ド されますが、その出力は Unicode 文字列です) で、pickle はバイナリ
  の直 列化フォーマットです;

* JSON は人間が読める形式ですが、pickle はそうではありません;

* JSON は相互運用可能で Python 以外でも広く使用されていますが、
  pickle は Python 固有です;

* JSON は、デフォルトでは Python の組み込み型の一部しか表現すること
  が できず、カスタムクラスに対しても行えません; pickle は極めて多くの
  Python 組み込み型を表現できます (その多くは賢い Python 内省機構によ
  って自動的に行われます; 複雑なケースでは 固有のオブジェクト API によ
  って対応できます)。

参考: "json" モジュール: JSON への直列化および復元を行うための標準ラ
  イブラ リモジュール。


12.1.2. データストリームの形式
==============================

"pickle" によって使用されるデータフォーマットは Python 固有です。これ
は、JSON や XDR のような外部標準によって (例えばポインター共有を表わす
ことができないといったような) 制限を受けることがないという利点がありま
す; ただし、これは非 Python プログラムが pickle された Python オブジェ
クトを再構成することができないということも意味します。

デフォルトでは、"pickle" データフォーマットは比較的コンパクトなバイナ
リ表現を使用します。サイズの抑制目的の最適化が必要なら、pickel された
データを効率的に 圧縮する ことができます。

"pickletools" モジュールには "pickle" によって生成されたデータストリー
ムを解析するためのツールが含まれます。"pickletools" のソースコードには
、pickle プロトコルで使用される命令コードに関する詳細なコメントがあり
ます。

現在 pickle 化には 5 種類のプロトコルを使用できます。より高いプロトコ
ルを使用するほど、作成された pickle を読み込むためにより高い Python の
バージョンが必要になります。

* プロトコルバージョン 0 はオリジナルの「人間に判読可能な」プロトコ
  ル で、Python の初期のバージョンとの後方互換性を持ちます。

* プロトコルバージョン 1 は旧形式のバイナリフォーマットで、これも
  Python の初期バージョンと互換性があります。

* プロトコルバージョン 2 は Python 2.3 で導入されました。このバージ
  ョ ンでは *新方式のクラス* のより効率的な pickle 化を提供しました。
  プロ トコル 2 による改良に関する情報は **PEP 307** を参照してくださ
  い。

* プロトコルバージョン 3 は Python 3.0 で追加されました。このバージ
  ョ ンで "bytes" オブジェクトをサポートしました。これは Python 2.x で
  は 非 pickle 化できません。これはデフォルトのプロトコルで、他の
  Python 3 バージョンとの互換性が求められる場合の推奨プロトコルです。

* プロトコルバージョン 4 は Python 3.4 で追加されました。このバージ
  ョ ンでは巨大なオブジェクトのサポート、より多くの種類のオブジェクト
  の pickle 化、および一部のデータ形式の最適化が行われました。プロトコ
  ル 4 による改良に関する情報は **PEP 3154** を参照してください。

注釈: 直列化は永続性より原始的な概念です。 "pickle" はファイルオブジ
  ェクト の読み書きを行いますが、永続オブジェクトの命名に関する問題に
  も、(さ らに困難な) 永続オブジェクトへの並列アクセスに関する問題にも
  対応しま せん。"pickle" モジュールは複雑なオブジェクトをバイトストリ
  ームに変 換し、バイトストリームから同じ内部構造のオブジェクトに復元
  することが できます。これらのバイトストリームはファイルに出力される
  ことが多いで しょうが、ネットワークを介して送信したり、データベース
  に格納すること もありえます。"shelve" モジュールは、オブジェクトを
  DBM 方式のデータ ベースファイル上で pickle 化および非 pickle 化する
  シンプルなインター フェイスを提供します。


12.1.3. モジュールインタフェース
================================

オブジェクト階層を直列化するには、"dumps()" 関数を呼ぶだけです。同様に
、データストリームを復元するには、"loads()" 関数を呼びます。しかし、直
列化および復元に対してより多くのコントロールを行いたい場合、それぞれ
"Pickler" または "Unpickler" オブジェクトを作成することができます。

"pickle" モジュールは以下の定数を提供しています:

pickle.HIGHEST_PROTOCOL

   利用可能なうち最も高い プロトコルバージョン (整数)。この値は
   *protocol* 値として関数 "dump()"  および "dumps()" と、"Pickler" コ
   ンストラクターに渡すことができます。

pickle.DEFAULT_PROTOCOL

   pickle 化で使用されるデフォルトの プロトコルバージョン (整数)。
   "HIGHEST_PROTOCOL" より低い値になる場合があります。現在のデフォルト
   プロトコルは Python 3 用に設計された 3 です。

この pickle 化の手続きを便利にするために、 "pickle" モジュールでは以下
の関数を提供しています:

pickle.dump(obj, file, protocol=None, *, fix_imports=True)

   *obj* を pickle 化し、すでにオープンしている *ファイルオブジェクト*
   *file* に書き込みます。"Pickler(file, protocol).dump(obj)" と等価で
   す。

   任意の引数 *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 でも
   読み込み可能です。

pickle.dumps(obj, protocol=None, *, fix_imports=True)

   ファイルに書く代わりに、"bytes" オブジェクトとしてオブジェクトの
   pickle 表現を返します。

   引数 *protocol* および *fix_imports* は "dump()" では同じ意味になり
   ます。

pickle.load(file, *, fix_imports=True, encoding="ASCII", errors="strict")

   pickle オブジェクト表現を、オープンしている *ファイルオブジェクト*
   *file* から読み込み、その中で指定されているオブジェクト階層に再構成
   して返します。これは "Unpickler(file).load()" と等価です。

   pickle のプロトコルバージョンは自動的に検出されます。したがって
   protocol 引数は必要ありません。pickle 化オブジェクト表現より後のバ
   イト列は無視されます。

   引数 *file* には 2 つのメソッド、整数引数をとる read() と、引数を要
   求しない readline() メソッドがなければなりません。両方のメソッドは
   バイト列を返す必要があります。そのため *file* には、バイナリ読み込
   みとしてオープンされたディスク上のファイル、"io.BytesIO" オブジェク
   ト、あるいはこのインタフェースに適合するその他のカスタムオブジェク
   トなどを指定できます。

   Optional keyword arguments are *fix_imports*, *encoding* and
   *errors*, which are used to control compatibility support for
   pickle stream generated by Python 2.  If *fix_imports* is true,
   pickle will try to map the old Python 2 names to the new names used
   in Python 3.  The *encoding* and *errors* tell pickle how to decode
   8-bit string instances pickled by Python 2; these default to
   'ASCII' and 'strict', respectively.  The *encoding* can be 'bytes'
   to read these 8-bit string instances as bytes objects. Using
   "encoding='latin1'" is required for unpickling NumPy arrays and
   instances of "datetime", "date" and "time" pickled by Python 2.

pickle.loads(bytes_object, *, fix_imports=True, encoding="ASCII", errors="strict")

   "bytes" オブジェクトから pickle 化されたオブジェクト階層を読み込ん
   で、その中で指定されたオブジェクト階層に再構成して返します。

   pickle のプロトコルバージョンは自動的に検出されます。したがって
   protocol 引数は必要ありません。pickle 化オブジェクト表現より後のバ
   イト列は無視されます。

   Optional keyword arguments are *fix_imports*, *encoding* and
   *errors*, which are used to control compatibility support for
   pickle stream generated by Python 2.  If *fix_imports* is true,
   pickle will try to map the old Python 2 names to the new names used
   in Python 3.  The *encoding* and *errors* tell pickle how to decode
   8-bit string instances pickled by Python 2; these default to
   'ASCII' and 'strict', respectively.  The *encoding* can be 'bytes'
   to read these 8-bit string instances as bytes objects. Using
   "encoding='latin1'" is required for unpickling NumPy arrays and
   instances of "datetime", "date" and "time" pickled by Python 2.

"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" モジュールでは、2 つのクラス "Pickler" および "Unpickler" を
提供しています:

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

   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 でも
   読み込み可能です。

   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 で追加.

   fast

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

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

class pickle.Unpickler(file, *, fix_imports=True, encoding="ASCII", errors="strict")

   これは pickle データストリームの読み込みのためにバイナリファイルを
   とります。

   pickle のプロトコルバージョンは自動的に検出されます。したがって
   protocol 引数は必要ありません。

   引数 *file* には2つのメソッド、整数引数をとる read() メソッドと、引
   数を要求しない readline() 引数がなければなりません。両方のメソッド
   はバイトを返す必要があります。そのため *file* には、バイナリ読み込
   みのために開かれたディスク上のファイルオブジェクト、 "io.BytesIO"
   オブジェクト、あるいはこのインタフェースに適合するその他のカスタム
   オブジェクトなどを指定することが可能です。

   任意のキーワード引数 *fix_imports*、*encoding* および *errors* は
   Python 2 で作成された pickle ストリームとの互換性を制御するために使
   用します。*fix_imports* が真の場合、pickle は古い Python 2 の名前と
   Python 3 で使用されている新しい名前との対応付けを試みます。
   *encoding* と *errors* は Python 2 で pickle 化された 8 ビット文字
   列インスタンスのデコード方法を指定します; デフォルトではそれぞれ
   'ASCII' および 'strict' になります。8 ビット文字列インスタンスをバ
   イト列オブジェクトとして読み込みたい場合には、*encoding* に 'bytes'
   を指定します。

   load()

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

   persistent_load(pid)

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

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

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

   find_class(module, name)

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

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


12.1.4. pickle 化、非 pickle 化できるもの
=========================================

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

* "None" 、 "True" 、および "False"

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

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

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

* モジュールのトップレベルで定義された関数 ("def" で定義されたものの
  み で "lambda" で定義されたものは含まない)

* モジュールのトップレベルで定義されている組込み関数

* モジュールのトップレベルで定義されているクラス

* "__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__()" メソッドによって適切な変換が行わ
れるようにオブジェクトのバージョン番号を入れておくとよいかもしれません
。


12.1.5. クラスインスタンスの 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__()

   In protocols 2 and newer, classes that implements the
   "__getnewargs_ex__()" method can dictate the values passed to the
   "__new__()" method upon unpickling.  The method must return a pair
   "(args, kwargs)" where *args* is a tuple of positional arguments
   and *kwargs* a dictionary of named arguments for constructing the
   object.  Those will be passed to the "__new__()" method upon
   unpickling.

   クラスの "__new__()" メソッドにキーワード引数のみ定義されている場合
   はこのメソッドを実装すべきです。そうしない場合、互換性のため
   "__getnewargs__()" メソッドの実装を推奨します。

   バージョン 3.6 で変更: "__getnewargs_ex__()" is now used in
   protocols 2 and 3.

object.__getnewargs__()

   This method serves a similar purpose as "__getnewargs_ex__()", but
   supports only positional arguments.  It must return a tuple of
   arguments "args" which will be passed to the "__new__()" method
   upon unpickling.

   "__getnewargs__()" will not be called if "__getnewargs_ex__()" is
   defined.

   バージョン 3.6 で変更: Before Python 3.6, "__getnewargs__()" was
   called instead of "__getnewargs_ex__()" in protocols 2 and 3.

object.__getstate__()

   クラスはそのインスタンスをどう pickle 化するかについてさらに影響を
   与えることができます; クラスに "__getstate__()" メソッドが定義され
   ていた場合それが呼ばれ、返り値のオブジェクトはインスタンスの辞書で
   はなく、インスタンスの内容が pickle 化されたものになります。
   "__getstate__()" がないときは通常通りインスタンスの "__dict__" が
   pickle 化されます。

object.__setstate__(state)

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

   注釈: "__getstate__()" が偽値を返す場合、非 pickle 化時に
     "__setstate__()" メソッドは呼ばれません。

"__getstate__()" および "__setstate__()" メソッドの使い方に関する詳細
な情報については 状態を持つオブジェクトの扱い 節を参照してください。

注釈: 非 pickle 化時、"__getattr__()"、"__getattribute__()" あるいは
  "__setattr__()" のような一部のメソッドがインスタンス上で呼び出される
  ことがあります。この場合、これらのメソッドはいくつかの内部不変条件が
  真であることに依存しており、そのような不変条件を立証するために、デー
  タ型には "__getnewargs__()" または "__getnewargs_ex__()" が実装され
  ていなければなりません; さもなくば、"__new__()" も "__init__()" も呼
  び出されません。

これらから見るように、pickle は上記のメソッドを直接使用しません。実際
には、これらのメソッドは "__reduce__()" 特殊メソッドを実装するコピープ
ロトコルの一部です。コピープロトコルは、pickle 化とオブジェクトのコピ
ーに必要な、データを取得するための統一されたインタフェースを提供します
。 [4]

強力ですが、クラスに "__reduce__()" メソッドを直接実装することはエラー
を起こしやすくなります。この理由のため、クラスの設計者は可能なかぎり高
レベルインタフェース ("__getnewargs_ex__()"、"__getstate__()" および
"__setstate__()") を使用するべきです。公開はしているものの、
"__reduce__()" の使用は、あくまでオプションとして、より効果的な pickle
化につながる場合、あるいはその両方の場合のみにしてください。

object.__reduce__()

   このインタフェースは現在、以下のように定義されています。
   "__reduce__()" メソッドは引数を取らず、文字列あるいは (こちらの方が
   好まれますが) タプルのいずれかを返すべきです (返されたオブジェクト
   は、しばしば "reduce value" と呼ばれます)。

   文字列が返された場合、その文字列はグローバル変数の名前として解釈さ
   れます。それはオブジェクトのモジュールから見たローカル名であるべき
   です; pickle モジュールは、オブジェクトのモジュールを決定するために
   モジュールの名前空間を検索します。この振る舞いは、典型的にシングル
   トンで便利です。

   タプルが返された場合、それは 2〜5 要素長でなければなりません。オプ
   ションのアイテムは省略することができます。あるいはそれらの値として
   "None" を渡すことができます。各要素の意味は順に:

   * オブジェクトの初期バージョンを作成するために呼ばれる呼び出し可
     能 オブジェクト。

   * 呼出し可能オブジェクトに対する引数のタプル。呼出し可能オブジェ
     ク トが引数を受け取らない場合、空のタプルが与えられなければなりま
     せ ん。

   * 任意で、前述のオブジェクトの "__setstate__()" メソッドに渡され
     る オブジェクトの状態。オブジェクトがそのようなメソッドを持たない
     場 合、値は辞書でなければならず、それはオブジェクトの "__dict__"
     属 性に追加されます。

   * 任意で、連続した要素を yield する (シーケンスではなく) イテレー
     タ ー。これらの要素は "obj.append(item)" を使用して、あるいはバッ
     チ では "obj.extend(list_of_items)" を使用して、オブジェクトに追
     加さ れます。これは主としてリストのサブクラスに対して使用されます
     が、 適切なシグネチャを持つ "append()" および "extend()" メソッド
     があ るかぎり、他のクラスで使用することもできます。 ("append()"
     または "extend()" のどちらが使用されるかは、どの pickle プロトコ
     ルバージ ョンが使われるかに加えて追加されるアイテムの数にも依存し
     ます。し たがって、両方をサポートする必要があります)

   * 任意で、連続する key-value ペアを yield する (シーケンスでなく)
     イテレーター。これらの要素は "obj[key] = value" を使用して、オブ
     ジェクトに格納されます。これは主として辞書のサブクラスに対して使
     用されますが、"__setitem__()" を実装しているかぎり他のクラスで使
     用することもできます。

object.__reduce_ex__(protocol)

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


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

オブジェクトの永続化のために、"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()


12.1.5.2. ディスパッチテーブル
------------------------------

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)


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

ここでは、クラスを 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!'


12.1.6. グローバル変数を制限する
================================

デフォルトで、非 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 や、サードパーティのソリューションのような別の選択肢を考慮した方が
よいでしょう。


12.1.7. 性能
============

pickle プロトコルの最近のバージョン (プロトコル 2 以降) は一部の一般的
な機能と組み込みデータ型を効率的にバイナリにエンコードするよう考慮され
ています。また、"pickle" モジュールは C 言語で書かれた透過的オプティマ
イザーを持っています。


12.1.8. 使用例
==============

最も単純なコードでは、"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" モジュール
     pickle データの処理や分析を行うためのツール。

  "shelve" モジュール
     オブジェクトのインデクス付きデータベース; "pickle" を使います。

  "copy" モジュール
     オブジェクトの浅いコピーおよび深いコピー。

  "marshal" モジュール
     組み込み型の高性能な直列化。

-[ 脚注 ]-

[1] "marshal" モジュールと間違えないように注意してください。

[2] なぜ "lambda" 関数を pickle 化できないかというと、すべての
    "lambda" 関数は同じ名前: "<lambda>" を共有しているからです。

[3] 送出される例外は "ImportError" や "AttributeError" になるはず
    です が、他の例外も起こりえます。

[4] "copy" モジュールは、浅いコピーと深いコピーの操作にこのプロト
    コル を使用します。

[5] 英数文字に関する制限は、プロトコル 0 では永続的な ID が改行文
    字に よって区切られるという事実によります。そのため、永続的な ID
    に何ら かの改行文字が含まれると、結果として生じる pickle は判読不
    能になり ます。
