8.11. "weakref" --- 弱参照
**************************

バージョン 2.1 で追加.

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

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

"weakref" モジュールは、Pythonプログラマがオブジェクトへの弱参照
(*weak refarence*)を作成できるようにします。

以下では、リファレント (*referent*) という用語は弱参照が参照するオブジ
ェクトを意味します。

オブジェクトに対する弱参照は、そのオブジェクトを生かしておく十分条件に
はなりません: あるリファレントに対する参照が弱参照しか残っていない場合
、ガベージコレクション (*garbage collection*)機構は自由にリファレント
を破壊し、そのメモリを別の用途に再利用できます。弱参照の主な用途は、巨
大なオブジェクトを保持するキャッシュやマップ型の実装において、キャッシ
ュやマップ型にあるという理由だけオブジェクトを存続させたくない場合です
。

例えば、巨大なバイナリ画像のオブジェクトがたくさんあり、それぞれに名前
を関連付けたいとします。 Python の辞書型を使って名前を画像に対応付けた
り画像を名前に対応付けたりすると、画像オブジェクトは辞書内のキーや値に
使われているため存続しつづけることになります。 "weakref" モジュールが
提供している "WeakKeyDictionary" や "WeakValueDictionary" クラスはその
代用で、対応付けを構築するのに弱参照を使い、キャッシュやマッピングに存
在するという理由だけでオブジェクトを存続させないようにします。例えば、
もしある画像オブジェクトが "WeakValueDictionary" の値になっていた場合
、最後に残った画像オブジェクトへの参照を弱参照マッピングが保持していれ
ば、ガーベジコレクションはこのオブジェクトを再利用でき、画像オブジェク
トに対する弱参照内の対応付けは削除されます。

"WeakKeyDictionary" や "WeakValueDictionary" は弱参照を使って実装され
ていて、キーや値がガベージコレクションによって回収されたことを弱参照辞
書に知らせるような弱参照オブジェクトのコールバック関数を設定しています
。ほとんどのプログラムが、いずれかの弱参照辞書型を使うだけで必要を満た
せるはずです --- 自作の弱参照辞書を直接作成する必要は普通はありません
。とはいえ、高度な使い方をするために、弱参照辞書の実装に使われている低
水準の機構は "weakref" モジュールで公開されています。

すべてのオブジェクトを弱参照できるわけではありません。弱参照できるオブ
ジェクトは、クラスインスタンス、(Cではなく) Pythonで書かれた関数、(束
縛および非束縛の両方の) メソッド、 "set" と "frozenset" 型、ファイルオ
ブジェクト、ジェネレータ(*generator*)、 type オブジェクト、 "bsddb" モ
ジュールの "DBcursor" 型、ソケット型、 "array" 型、 "deque" 型、正規表
現パターンオブジェクト, code オブジェクトです。

バージョン 2.4 で変更: ファイル、ソケット、 "array" 、および正規表現パ
ターンのサポートを追加しました.

バージョン 2.7 で変更: thread.lock, threading.Lock, code オブジェクト
のサポートが追加されました。

"list" や "dict" など、いくつかの組み込み型は弱参照を直接サポートしま
せんが、以下のようにサブクラス化を行えばサポートを追加できます:

   class Dict(dict):
       pass

   obj = Dict(red=1, green=2, blue=3)   # this object is weak referenceable

"tuple" や "long" など、他の組み込み型はサブクラス化しても弱参照をサポ
ートしません。

拡張型は、簡単に弱参照をサポートできます。詳細については、 弱参照(Weak
Reference)のサポート を参照してください。

class weakref.ref(object[, callback])

   *object* への弱参照を返します。リファレントがまだ生きているならば、
   元のオブジェクトは参照オブジェクトの呼び出しで取り出せす。リファレ
   ントがもはや生きていないならば、参照オブジェクトを呼び出したときに
   "None" を返します。 *callback* に "None" 以外の値を与えた場合、オブ
   ジェクトをまさに後始末処理しようとするときに呼び出します。このとき
   弱参照オブジェクトは *callback* の唯一のパラメタとして渡されます。
   リファレントはもはや利用できません。

   同じオブジェクトに対してたくさんの弱参照を作れます。それぞれの弱参
   照に対して登録されたコールバックは、もっとも新しく登録されたコール
   バックからもっとも古いものへと呼び出されます。

   コールバックが発生させた例外は標準エラー出力に書き込まれますが、伝
   播されません。それらはオブジェクトの "__del__()" メソッドが発生させ
   る例外と完全に同じ方法で処理されます。

   *object* がハッシュ可能(*hashable*)ならば、弱参照はハッシュ可能です
   。それらは *object* が削除された後でもそれらのハッシュ値を保持しま
   す。 *object* が削除されてから初めて "hash()" が呼び出された場合に
   、その呼び出しは "TypeError" を発生させます。

   弱参照は等価性のテストをサポートしていますが、順序をサポートしてい
   ません。参照がまだ生きているならば、 *callback* に関係なく二つの参
   照はそれらのリファレントと同じ等価関係を持ちます。リファレントのど
   ちらか一方が削除された場合、参照オブジェクトが同一である場合に限り
   、その参照は等価です。

   バージョン 2.4 で変更: 以前はファクトリでしたが、サブクラス化可能な
   型になりました。 "object" 型から派生しています.

weakref.proxy(object[, callback])

   弱参照を使う *object* へのプロキシを返します。弱参照オブジェクトを
   明示的な参照外しをしながら利用する代わりに、多くのケースでプロキシ
   を利用することができます。返されるオブジェクトは、 *object* が呼び
   出し可能かどうかによって、 "ProxyType" または "CallableProxyType"
   のどちらかの型を持ちます。プロキシオブジェクトはリファレントに関係
   なくハッシュ可能(*hashable*)ではありません。これによって、それらの
   基本的な変更可能という性質による多くの問題を避けています。そして、
   辞書のキーとしての利用を妨げます。 *callback* は "ref()" 関数の同じ
   名前のパラメータと同じものです。(--- 訳注: リファレントが変更不能型
   であっても、プロキシはリファレントが消えるという状態の変更があるた
   めに、変更可能型です。---)

weakref.getweakrefcount(object)

   *object* を参照する弱参照とプロキシの数を返します。

weakref.getweakrefs(object)

   *object* を参照するすべての弱参照とプロキシオブジェクトのリストを返
   します。

class weakref.WeakKeyDictionary([dict])

   キーを弱参照するマッピングクラス。キーへの強参照がなくなったときに
   、辞書のエントリは捨てられます。アプリケーションの他の部分が所有す
   るオブジェクトへ属性を追加することもなく、それらのオブジェクトに追
   加データを関連づけるために使うことができます。これは属性へのアクセ
   スをオーバーライドするオブジェクトに特に便利です。

   注釈: 警告: "WeakKeyDictionary" は Python 辞書型の上に作られてい
     るので 、反復処理を行うときにはサイズ変更してはなりません。
     "WeakKeyDictionary" の場合、反復処理の最中にプログラムが行った操
     作が、(ガベージコレクションの副作用として) 「魔法のように」辞書内
     の要素を消し去ってしまうため、確実なサイズ変更は困難なのです。

"WeakKeyDictionary" オブジェクトは、以下の追加のメソッドを持ちます。こ
れらのメソッドは、内部の参照を直接公開します。その参照は、利用される時
に生存しているとは限りません。なので、参照を利用する前に、その参照をチ
ェックする必要があります。これにより、必要なくなったキーの参照が残って
いるために、ガベージコレクタがそのキーを削除できなくなる事態を避ける事
ができます。

WeakKeyDictionary.iterkeyrefs()

   キーへの弱参照を持つ iterable オブジェクトを返します。

   バージョン 2.5 で追加.

WeakKeyDictionary.keyrefs()

   キーへの弱参照のリストを返します。

   バージョン 2.5 で追加.

class weakref.WeakValueDictionary([dict])

   値を弱参照するマッピングクラス。値への強参照が存在しなくなったとき
   に、辞書のエントリは捨てられます。

   注釈: 警告: "WeakValueDictionary" は Python 辞書型の上に作られて
     いるの で、反復処理を行うときにはサイズ変更してはなりません。
     "WeakValueDictionary" の場合、反復処理の最中にプログラムが行った
     操作が、(ガベージコレクションの副作用として) 「魔法のように」辞書
     内の要素を消し去ってしまうため、確実なサイズ変更は困難なのです。

"WeakValueDictionary" オブジェクトは次のメソッドも備えています。 これ
らのメソッドは、 "WeakKeyDictionary" オブジェクトの "iterkeyrefs()" メ
ソッドや "keyrefs()" メソッドと同じ問題を抱えています。

WeakValueDictionary.itervaluerefs()

   値への弱参照を持つ iterable オブジェクトを返します。

   バージョン 2.5 で追加.

WeakValueDictionary.valuerefs()

   値への弱参照のリストを返します。

   バージョン 2.5 で追加.

class weakref.WeakSet([elements])

   要素への弱参照を持つ集合型。要素への強参照が無くなったときに、その
   要素は削除されます。

   バージョン 2.7 で追加.

weakref.ReferenceType

   弱参照オブジェクトのための型オブジェクト。

weakref.ProxyType

   呼び出し可能でないオブジェクトのプロキシのための型オブジェクト。

weakref.CallableProxyType

   呼び出し可能なオブジェクトのプロキシのための型オブジェクト。

weakref.ProxyTypes

   プロキシのためのすべての型オブジェクトを含むシーケンス。これは両方
   のプロキシ型の名前付けに依存しないで、オブジェクトがプロキシかどう
   かのテストをより簡単にできます。

exception weakref.ReferenceError

   プロキシオブジェクトが使われても、元のオブジェクトがガベージコレク
   ションされてしまっているときに発生する例外。これは標準の
   "ReferenceError" 例外と同じです。

参考:

  **PEP 205** - 弱参照
     この機能の提案と理論的根拠。初期の実装と他の言語における類似の機
     能についての情報へのリンクを含んでいます。


8.11.1. 弱参照オブジェクト
==========================

弱参照オブジェクトは属性あるいはメソッドを持ちません。しかし、リファレ
ントがまだ存在するならば、呼び出すことでそのリファレントを取得できるよ
うにします:

>>> import weakref
>>> class Object:
...     pass
...
>>> o = Object()
>>> r = weakref.ref(o)
>>> o2 = r()
>>> o is o2
True

リファレントがもはや存在しないならば、参照オブジェクトの呼び出しは
"None" を返します:

>>> del o, o2
>>> print r()
None

弱参照オブジェクトがまだ生きているかどうかのテストは、式 "ref() is not
None" を用いて行われます。通常、参照オブジェクトを使う必要があるアプリ
ケーションコードはこのパターンに従います:

   # r is a weak reference object
   o = r()
   if o is None:
       # referent has been garbage collected
       print "Object has been deallocated; can't frobnicate."
   else:
       print "Object is still live!"
       o.do_something_useful()

"生存性(liveness)"のテストを分割すると、スレッド化されたアプリケーショ
ンにおいて競合状態を作り出します。 (訳注:"if r() is not None:
r().do_something()" では、2度目のr()がNoneを返す可能性があります) 弱参
照が呼び出される前に、他のスレッドは弱参照が無効になる原因となり得ます
。上で示したイディオムは、シングルスレッドのアプリケーションと同じくマ
ルチスレッド化されたアプリケーションにおいても安全です。

サブクラス化を行えば、 "ref" オブジェクトの特殊なバージョンを作成でき
ます。これは "WeakValueDictionary" の実装で使われており、マップ内の各
エントリによるメモリのオーバヘッドを減らしています。こうした実装は、あ
る参照に追加情報を関連付けたい場合に便利ですし、リファレントを取り出す
ための呼び出し時に何らかの追加処理を行いたい場合にも使えます。

以下の例では、 "ref" のサブクラスを使って、あるオブジェクトに追加情報
を保存し、リファレントがアクセスされたときにその値に作用をできるように
するための方法を示しています:

   import weakref

   class ExtendedRef(weakref.ref):
       def __init__(self, ob, callback=None, **annotations):
           super(ExtendedRef, self).__init__(ob, callback)
           self.__counter = 0
           for k, v in annotations.iteritems():
               setattr(self, k, v)

       def __call__(self):
           """Return a pair containing the referent and the number of
           times the reference has been called.
           """
           ob = super(ExtendedRef, self).__call__()
           if ob is not None:
               self.__counter += 1
               ob = (ob, self.__counter)
           return ob


8.11.2. 例
==========

この簡単な例では、アプリケーションが以前に参照したオブジェクトを取り出
すためにオブジェクトIDを利用する方法を示します。オブジェクトに生きたま
まであることを強制することなく、オブジェクトの IDを他のデータ構造の中
で使うことができ、必要に応じてIDからオブジェクトを取り出せます。

   import weakref

   _id2obj_dict = weakref.WeakValueDictionary()

   def remember(obj):
       oid = id(obj)
       _id2obj_dict[oid] = obj
       return oid

   def id2obj(oid):
       return _id2obj_dict[oid]
