8.11. weakref — 弱参照

バージョン 2.1 で追加.

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


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

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

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

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

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

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

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

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

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

class Dict(dict):
    pass

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

CPython 実装の詳細: tuplelong など、他の組み込み型はサブクラス化しても弱参照をサポートしません。

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

class weakref.ref(object[, callback])

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

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

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

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

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

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

weakref.proxy(object[, callback])

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

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]