weakref --- 弱参照

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


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

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

オブジェクトへの弱参照があることは、そのオブジェクトを生かしておくのには不十分です。リファレントへの参照が弱参照しか残っていない場合、 garbage collection はリファレントを自由に破棄し、メモリを別のものに再利用することができます。しかし、オブジェクトへの強参照がなくても、オブジェクトが実際に破棄されるまでは、弱参照はオブジェクトを返す場合があります。

弱参照の主な用途は、巨大なオブジェクトを保持するキャッシュやマッピングを実装することです。ここで、キャッシュやマッピングに保持されているからという理由だけで、巨大なオブジェクトが生き続けることは望ましくありません。

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

WeakKeyDictionaryWeakValueDictionary はその実装に弱参照を使用しており、キーや値がガーベジコレクションによって回収されたことを弱参照辞書に通知するコールバック関数を設定しています。 WeakSetset インターフェースを実装していますが、 WeakKeyDictionary のように要素への弱参照を持ちます。

finalize は、オブジェクトのガベージコレクションの実行時にクリーンアップ関数が呼び出されるように登録する、単純な方法を提供します。これは、未加工の弱参照上にコールバック関数を設定するよりも簡単です。なぜなら、オブジェクトのコレクションが完了するまでファイナライザが生き続けることを、モジュールが自動的に保証するからです。

ほとんどのプログラムでは弱参照コンテナまたは finalize のどれかを使えば必要なものは揃うはずです。通常は直接自前の弱参照を作成する必要はありません。低レベルな機構は、より進んだ使い方をするために weakref モジュールとして公開されています。

全てのオブジェクトが弱参照で参照できるわけではありません。弱参照をサポートするオブジェクトは、クラスインスタンス、(C ではなく) Python で書かれた関数、インスタンスメソッド、set オブジェクト、frozenset オブジェクト、 file オブジェクトgenerators 型のオブジェクト、socket オブジェクト、array オジェクト、deque オブジェクト、正規表現パターンオブジェクト、code オブジェクトです。

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

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

class Dict(dict):
    pass

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

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

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

When __slots__ are defined for a given type, weak reference support is disabled unless a '__weakref__' string is also present in the sequence of strings in the __slots__ declaration. See __slots__ documentation for details.

class weakref.ref(object[, callback])

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

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

Exceptions raised by the callback will be noted on the standard error output, but cannot be propagated; they are handled in exactly the same way as exceptions raised from an object's __del__() method.

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

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

これはサブクラス化可能な型というよりファクトリ関数です。

__callback__

この読み出し専用の属性は、現在弱参照に関連付けられているコールバックを返します。コールバックが存在しないか、弱参照のリファレントが生きていない場合、この属性の値は None になります。

バージョン 3.4 で変更: __callback__ 属性が追加されました。

weakref.proxy(object[, callback])

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

Accessing an attribute of the proxy object after the referent is garbage collected raises ReferenceError.

バージョン 3.8 で変更: Extended the operator support on proxy objects to include the matrix multiplication operators @ and @=.

weakref.getweakrefcount(object)

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

weakref.getweakrefs(object)

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

class weakref.WeakKeyDictionary([dict])

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

Note that when a key with equal value to an existing key (but not equal identity) is inserted into the dictionary, it replaces the value but does not replace the existing key. Due to this, when the reference to the original key is deleted, it also deletes the entry in the dictionary:

>>> class T(str): pass
...
>>> k1, k2 = T(), T()
>>> d = weakref.WeakKeyDictionary()
>>> d[k1] = 1   # d = {k1: 1}
>>> d[k2] = 2   # d = {k1: 2}
>>> del k1      # d = {}

A workaround would be to remove the key prior to reassignment:

>>> class T(str): pass
...
>>> k1, k2 = T(), T()
>>> d = weakref.WeakKeyDictionary()
>>> d[k1] = 1   # d = {k1: 1}
>>> del d[k1]
>>> d[k2] = 2   # d = {k2: 2}
>>> del k1      # d = {k2: 2}

バージョン 3.9 で変更: Added support for | and |= operators, specified in PEP 584.

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

WeakKeyDictionary.keyrefs()

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

class weakref.WeakValueDictionary([dict])

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

バージョン 3.9 で変更: PEP 584 で規定されている | 演算子と |= 演算子のサポートを追加しました。

WeakValueDictionary objects have an additional method that has the same issues as the WeakKeyDictionary.keyrefs() method.

WeakValueDictionary.valuerefs()

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

class weakref.WeakSet([elements])

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

class weakref.WeakMethod(method[, callback])

拡張された ref のサブクラスで、束縛されたメソッドへの弱参照をシミュレートします (つまり、クラスで定義され、インスタンスにあるメソッド)。 束縛されたメソッドは短命なので、標準の弱参照では保持し続けられません。 WeakMethod には、オブジェクトと元々の関数が死ぬまで束縛されたメソッドを再作成する特別なコードがあります:

>>> class C:
...     def method(self):
...         print("method called!")
...
>>> c = C()
>>> r = weakref.ref(c.method)
>>> r()
>>> r = weakref.WeakMethod(c.method)
>>> r()
<bound method C.method of <__main__.C object at 0x7fc859830220>>
>>> r()()
method called!
>>> del c
>>> gc.collect()
0
>>> r()
>>>

callback is the same as the parameter of the same name to the ref() function.

バージョン 3.4 で追加.

class weakref.finalize(obj, func, /, *args, **kwargs)

obj がガベージコレクションで回収されるときに呼び出される、呼び出し可能なファイナライザオブジェクトを返します。 通常の弱参照とは異なり、ファイナライザは参照しているオブジェクトが回収されるまで必ず生き残り、そのおかげでライフサイクル管理が大いに簡単になります。

ファイナライザは (直接もしくはガベージコレクションのときに) 呼び出されるまで 生きている と見なされ、呼び出された後には 死んでいます 。 生きているファイナライザを呼び出すと、 func(*arg, **kwargs) を評価した結果を返します。一方、死んでいるファイナライザを呼び出すと None を返します。

Exceptions raised by finalizer callbacks during garbage collection will be shown on the standard error output, but cannot be propagated. They are handled in the same way as exceptions raised from an object's __del__() method or a weak reference's callback.

プログラムが終了するとき、生き残ったそれぞれのファイナライザは、自身の atexit 属性が偽に設定されるまで呼び出され続けます。 ファイナライザは生成された順序の逆順で呼び出されます。

A finalizer will never invoke its callback during the later part of the interpreter shutdown when module globals are liable to have been replaced by None.

__call__()

If self is alive then mark it as dead and return the result of calling func(*args, **kwargs). If self is dead then return None.

detach()

If self is alive then mark it as dead and return the tuple (obj, func, args, kwargs). If self is dead then return None.

peek()

If self is alive then return the tuple (obj, func, args, kwargs). If self is dead then return None.

alive

ファイナライザが生きている場合には真、そうでない場合には偽のプロパティです。

atexit

A writable boolean property which by default is true. When the program exits, it calls all remaining live finalizers for which atexit is true. They are called in reverse order of creation.

注釈

It is important to ensure that func, args and kwargs do not own any references to obj, either directly or indirectly, since otherwise obj will never be garbage collected. In particular, func should not be a bound method of obj.

バージョン 3.4 で追加.

weakref.ReferenceType

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

weakref.ProxyType

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

weakref.CallableProxyType

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

weakref.ProxyTypes

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

参考

PEP 205 - 弱参照

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

弱参照オブジェクト

Weak reference objects have no methods and no attributes besides ref.__callback__. A weak reference object allows the referent to be obtained, if it still exists, by calling it:

>>> 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().__init__(ob, callback)
        self.__counter = 0
        for k, v in annotations.items():
            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().__call__()
        if ob is not None:
            self.__counter += 1
            ob = (ob, self.__counter)
        return ob

使用例

この簡単な例では、アプリケーションが以前に参照したオブジェクトを取り出すためにオブジェクト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]

ファイナライザオブジェクト

The main benefit of using finalize is that it makes it simple to register a callback without needing to preserve the returned finalizer object. For instance

>>> import weakref
>>> class Object:
...     pass
...
>>> kenny = Object()
>>> weakref.finalize(kenny, print, "You killed Kenny!")  
<finalize object at ...; for 'Object' at ...>
>>> del kenny
You killed Kenny!

ファイナライザは直接呼び出すこともできます。ただし、ファイナライザはコールバックを最大でも一度しか呼び出しません。

>>> def callback(x, y, z):
...     print("CALLBACK")
...     return x + y + z
...
>>> obj = Object()
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
>>> assert f.alive
>>> assert f() == 6
CALLBACK
>>> assert not f.alive
>>> f()                     # callback not called because finalizer dead
>>> del obj                 # callback not called because finalizer dead

You can unregister a finalizer using its detach() method. This kills the finalizer and returns the arguments passed to the constructor when it was created.

>>> obj = Object()
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
>>> f.detach()                                           
(<...Object object ...>, <function callback ...>, (1, 2), {'z': 3})
>>> newobj, func, args, kwargs = _
>>> assert not f.alive
>>> assert newobj is obj
>>> assert func(*args, **kwargs) == 6
CALLBACK

Unless you set the atexit attribute to False, a finalizer will be called when the program exits if it is still alive. For instance

>>> obj = Object()
>>> weakref.finalize(obj, print, "obj dead or exiting")
<finalize object at ...; for 'Object' at ...>
>>> exit()
obj dead or exiting

Comparing finalizers with __del__() methods

インスタンスが一時ディレクトリを表す、クラスを作成するとします。そのディレクトリは、次のイベントのいずれかが起きた時に、そのディレクトリの内容とともに削除されるべきです。

  • オブジェクトのガベージコレクションが行われた場合

  • the object's remove() method is called, or

  • プログラムが終了した場合

We might try to implement the class using a __del__() method as follows:

class TempDir:
    def __init__(self):
        self.name = tempfile.mkdtemp()

    def remove(self):
        if self.name is not None:
            shutil.rmtree(self.name)
            self.name = None

    @property
    def removed(self):
        return self.name is None

    def __del__(self):
        self.remove()

Starting with Python 3.4, __del__() methods no longer prevent reference cycles from being garbage collected, and module globals are no longer forced to None during interpreter shutdown. So this code should work without any issues on CPython.

However, handling of __del__() methods is notoriously implementation specific, since it depends on internal details of the interpreter's garbage collector implementation.

A more robust alternative can be to define a finalizer which only references the specific functions and objects that it needs, rather than having access to the full state of the object:

class TempDir:
    def __init__(self):
        self.name = tempfile.mkdtemp()
        self._finalizer = weakref.finalize(self, shutil.rmtree, self.name)

    def remove(self):
        self._finalizer()

    @property
    def removed(self):
        return not self._finalizer.alive

Defined like this, our finalizer only receives a reference to the details it needs to clean up the directory appropriately. If the object never gets garbage collected the finalizer will still be called at exit.

The other advantage of weakref based finalizers is that they can be used to register finalizers for classes where the definition is controlled by a third party, such as running code when a module is unloaded:

import weakref, sys
def unloading_module():
    # implicit reference to the module globals from the function body
weakref.finalize(sys.modules[__name__], unloading_module)

注釈

If you create a finalizer object in a daemonic thread just as the program exits then there is the possibility that the finalizer does not get called at exit. However, in a daemonic thread atexit.register(), try: ... finally: ... and with: ... do not guarantee that cleanup occurs either.