weakref —弱参照—Pythonドキュメント

提供:Dev Guides
< PythonPython/docs/3.9/library/weakref
移動先:案内検索

weakref —弱い参照

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



weakref モジュールを使用すると、Pythonプログラマーはオブジェクトへの弱参照を作成できます。

以下では、指示対象という用語は、弱参照によって参照されるオブジェクトを意味します。

オブジェクトへの弱い参照は、オブジェクトを存続させるのに十分ではありません。参照先への残りの参照が弱い参照だけである場合、ガベージコレクションは自由に参照先を破棄し、そのメモリを他の目的に再利用します。 ただし、オブジェクトが実際に破棄されるまで、弱い参照は、強い参照がなくてもオブジェクトを返す場合があります。

弱参照の主な用途は、大きなオブジェクトを保持するキャッシュまたはマッピングを実装することです。大きなオブジェクトは、キャッシュまたはマッピングに表示されるという理由だけで存続させないことが望まれます。

たとえば、大きなバイナリイメージオブジェクトが多数ある場合は、それぞれに名前を関連付けることができます。 Pythonディクショナリを使用して名前を画像に、または画像を名前にマップした場合、画像オブジェクトは、ディクショナリに値またはキーとして表示されたという理由だけで存続します。 weakref モジュールによって提供される WeakKeyDictionary クラスと WeakValueDictionary クラスは代替手段であり、弱い参照を使用して、オブジェクトがマッピングオブジェクト。 たとえば、画像オブジェクトが WeakValueDictionary の値である場合、その画像オブジェクトへの最後の残りの参照が弱いマッピングによって保持されている弱い参照である場合、ガベージコレクションはオブジェクトとそれに対応するものを再利用できます。弱いマッピングのエントリは単に削除されます。

WeakKeyDictionary および WeakValueDictionary は、実装で弱参照を使用し、ガベージコレクションによってキーまたは値が再利用されたときに弱辞書に通知する弱参照にコールバック関数を設定します。 WeakSetset インターフェースを実装しますが、 WeakKeyDictionary と同様に、その要素への弱い参照を保持します。

finalize は、オブジェクトがガベージコレクションされたときに呼び出されるクリーンアップ関数を登録する簡単な方法を提供します。 これは、生の弱参照にコールバック関数を設定するよりも簡単に使用できます。これは、モジュールが、オブジェクトが収集されるまでファイナライザーが存続することを自動的に保証するためです。

ほとんどのプログラムは、これらの弱コンテナタイプの1つまたは finalize を使用するだけで十分であることがわかるはずです。通常、独自の弱参照を直接作成する必要はありません。 低レベルの機械は、高度な使用のために weakref モジュールによって公開されます。

すべてのオブジェクトを弱く参照できるわけではありません。 クラスインスタンス、Pythonで記述された関数(Cでは記述されていない)、インスタンスメソッド、セット、frozensets、一部のファイルオブジェクトジェネレーター、タイプオブジェクト、ソケット、配列を含むことができるオブジェクト、deques、通常の式パターンオブジェクト、およびコードオブジェクト。

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


listdict などのいくつかの組み込み型は、弱参照を直接サポートしていませんが、サブクラス化を通じてサポートを追加できます。

class Dict(dict):
    pass

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

弱参照をサポートするために拡張タイプを簡単に作成できます。 弱参照サポートを参照してください。

特定のタイプに対して__slots__が定義されている場合、'__weakref__'文字列が__slots__宣言の文字列のシーケンスにも存在しない限り、弱参照サポートは無効になります。 詳細については、 __slots__ドキュメントを参照してください。

class weakref.ref(object[, callback])

オブジェクトへの弱参照を返します。 指示対象がまだ生きている場合は、参照オブジェクトを呼び出すことで元のオブジェクトを取得できます。 指示対象がもう生きていない場合、参照オブジェクトを呼び出すと、 None が返されます。 callback が提供され、 None が提供されておらず、返されたweakrefオブジェクトがまだ存続している場合、オブジェクトがファイナライズされようとしているときにコールバックが呼び出されます。 弱参照オブジェクトは、コールバックへの唯一のパラメーターとして渡されます。 指示対象は利用できなくなります。

同じオブジェクトに対して多くの弱参照を作成することは許容されます。 弱参照ごとに登録されたコールバックは、最後に登録されたコールバックから最も古い登録済みのコールバックに呼び出されます。

コールバックによって発生した例外は、標準エラー出力に記録されますが、伝播することはできません。 これらは、オブジェクトの__del__()メソッドから発生した例外とまったく同じ方法で処理されます。

オブジェクトがハッシュ可能である場合、弱参照はハッシュ可能です。 オブジェクトが削除された後でも、ハッシュ値は維持されます。 オブジェクトが削除された後でのみ hash()が最初に呼び出された場合、呼び出しによって TypeError が発生します。

弱参照は、同等性のテストをサポートしますが、順序付けはサポートしません。 指示対象がまだ生きている場合、2つの参照はそれらの指示対象と同じ同等関係を持ちます(コールバックに関係なく)。 いずれかの指示対象が削除されている場合、参照オブジェクトが同じオブジェクトである場合にのみ、参照は等しくなります。

これは、ファクトリ関数ではなく、サブクラス化可能なタイプです。

__callback__

この読み取り専用属性は、現在weakrefに関連付けられているコールバックを返します。 コールバックがない場合、またはweakrefの指示対象が存在しなくなった場合、この属性の値はNoneになります。

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

weakref.proxy(object[, callback])

弱参照を使用するオブジェクトにプロキシを返します。 これにより、弱参照オブジェクトで使用される明示的な逆参照を要求する代わりに、ほとんどのコンテキストでプロキシの使用がサポートされます。 返されるオブジェクトのタイプは、オブジェクトが呼び出し可能かどうかに応じて、ProxyTypeまたはCallableProxyTypeのいずれかになります。 プロキシオブジェクトは、指示対象に関係なくハッシュ可能ではありません。 これにより、基本的に変更可能な性質に関連する多くの問題が回避され、辞書キーとしての使用が妨げられます。 callback は、 ref()関数の同じ名前のパラメーターと同じです。

バージョン3.8で変更:プロキシオブジェクトの演算子サポートが拡張され、行列乗算演算子@および@=が含まれるようになりました。

weakref.getweakrefcount(object)
オブジェクトを参照する弱参照とプロキシの数を返します。
weakref.getweakrefs(object)
オブジェクトを参照するすべての弱参照およびプロキシオブジェクトのリストを返します。
class weakref.WeakKeyDictionary([dict])

キーを弱く参照するマッピングクラス。 キーへの強い参照がなくなると、辞書のエントリは破棄されます。 これを使用すると、オブジェクトに属性を追加せずに、アプリケーションの他の部分が所有するオブジェクトに追加のデータを関連付けることができます。 これは、属性アクセスをオーバーライドするオブジェクトで特に役立ちます。

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

WeakKeyDictionary オブジェクトには、内部参照を直接公開する追加のメソッドがあります。 参照は、使用時に「ライブ」であることが保証されていないため、参照を呼び出した結果を使用前に確認する必要があります。 これは、ガベージコレクターが必要以上にキーを保持する原因となる参照の作成を回避するために使用できます。

WeakKeyDictionary.keyrefs()
キーへの弱参照の反復可能オブジェクトを返します。
class weakref.WeakValueDictionary([dict])

値を弱く参照するマッピングクラス。 値への強い参照がなくなると、ディクショナリのエントリは破棄されます。

バージョン3.9で変更: PEP 584 で指定されているように、|および|=演算子のサポートが追加されました。

WeakValueDictionary オブジェクトには、 WeakKeyDictionary オブジェクトのkeyrefs()メソッドと同じ問題がある追加のメソッドがあります。

WeakValueDictionary.valuerefs()
値への弱参照の反復可能オブジェクトを返します。
class weakref.WeakSet([elements])
要素への弱参照を保持するクラスを設定します。 要素への強い参照が存在しなくなると、要素は破棄されます。
class weakref.WeakMethod(method)

バインドされたメソッド(つまり、クラスで定義され、インスタンスで検索されたメソッド)への弱参照をシミュレートするカスタム 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()
>>>

バージョン3.4の新機能。

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

obj がガベージコレクションされたときに呼び出される呼び出し可能なファイナライザーオブジェクトを返します。 通常の弱参照とは異なり、ファイナライザーは参照オブジェクトが収集されるまで常に存続し、ライフサイクル管理を大幅に簡素化します。

ファイナライザーは、呼び出されるまで(明示的に、またはガベージコレクションで)生きていると見なされ、その後はデッドになります。 ライブファイナライザーを呼び出すと、func(*arg, **kwargs)の評価結果が返されますが、デッドファイナライザーを呼び出すと、 None が返されます。

ガベージコレクション中にファイナライザーコールバックによって発生した例外は、標準エラー出力に表示されますが、伝播することはできません。 これらは、オブジェクトの__del__()メソッドまたは弱参照のコールバックから発生した例外と同じ方法で処理されます。

プログラムが終了すると、 atexit 属性がfalseに設定されていない限り、残りの各ライブファイナライザーが呼び出されます。 それらは作成の逆の順序で呼び出されます。

モジュールグローバルが None に置き換えられる可能性がある場合、ファイナライザーはインタープリターシャットダウンの後半でコールバックを呼び出すことはありません。

__call__()

self が生きている場合は、それをデッドとしてマークし、func(*args, **kwargs)を呼び出した結果を返します。 self が停止している場合は、 None を返します。

detach()

self が生きている場合は、それをデッドとしてマークし、タプル(obj, func, args, kwargs)を返します。 self が停止している場合は、 None を返します。

peek()

self が生きている場合は、タプル(obj, func, args, kwargs)を返します。 self が停止している場合は、 None を返します。

alive

ファイナライザーが有効な場合はtrue、それ以外の場合はfalseのプロパティ。

atexit

デフォルトでtrueである書き込み可能なブールプロパティ。 プログラムが終了すると、 atexit がtrueである残りのすべてのライブファイナライザーが呼び出されます。 それらは作成の逆の順序で呼び出されます。

ノート

funcargs 、および kwargs が、直接的または間接的に obj への参照を所有していないことを確認することが重要です。 obj がガベージコレクションされることはありません。 特に、 funcobj のバインドされたメソッドであってはなりません。

バージョン3.4の新機能。

weakref.ReferenceType
弱参照オブジェクトの型オブジェクト。
weakref.ProxyType
呼び出し可能ではないオブジェクトのプロキシのタイプオブジェクト。
weakref.CallableProxyType
呼び出し可能オブジェクトのプロキシの型オブジェクト。
weakref.ProxyTypes
プロキシのすべての型オブジェクトを含むシーケンス。 これにより、両方のプロキシタイプの名前に依存することなく、オブジェクトがプロキシであるかどうかを簡単にテストできます。

も参照してください

PEP 205 -弱い参照
以前の実装へのリンクや他の言語の同様の機能に関する情報など、この機能の提案と理論的根拠。


弱い参照オブジェクト

弱参照オブジェクトには、 ref .__ callback __ 以外にメソッドと属性がありません。 弱参照オブジェクトを使用すると、参照対象がまだ存在する場合は、それを呼び出すことで取得できます。

>>> 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()

「活性」について別のテストを使用すると、スレッド化されたアプリケーションで競合状態が発生します。 別のスレッドにより、弱参照が呼び出される前に弱参照が無効になる可能性があります。 上記のイディオムは、スレッド化されたアプリケーションだけでなく、シングルスレッド化されたアプリケーションでも安全です。

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]

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

finalize を使用する主な利点は、返されたファイナライザーオブジェクトを保持しなくても、コールバックを簡単に登録できることです。 例えば

>>> 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!

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

>>> 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

detach()メソッドを使用して、ファイナライザーの登録を解除できます。 これにより、ファイナライザーが強制終了され、作成時にコンストラクターに渡された引数が返されます。

>>> 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

atexit 属性を False に設定しない限り、プログラムがまだ生きている場合は、プログラムの終了時にファイナライザーが呼び出されます。 例えば

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

ファイナライザーと__del__()メソッドの比較

インスタンスが一時ディレクトリを表すクラスを作成するとします。 次のイベントの最初のイベントが発生した場合は、ディレクトリとその内容を削除する必要があります。

  • オブジェクトはガベージコレクションされ、
  • オブジェクトのremove()メソッドが呼び出される、または
  • プログラムは終了します。

次のように、__del__()メソッドを使用してクラスを実装しようとする場合があります。

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()

Python 3.4以降、__del__()メソッドは、参照サイクルのガベージコレクションを妨げなくなり、モジュールグローバルはインタープリターのシャットダウン中になしに強制されなくなりました。 したがって、このコードはCPythonで問題なく機能するはずです。

ただし、__del__()メソッドの処理は、インタープリターのガベージコレクター実装の内部詳細に依存するため、実装固有であることがよく知られています。

より堅牢な代替手段は、オブジェクトの完全な状態にアクセスするのではなく、必要な特定の関数とオブジェクトのみを参照するファイナライザーを定義することです。

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

このように定義されたファイナライザーは、ディレクトリを適切にクリーンアップするために必要な詳細への参照のみを受け取ります。 オブジェクトがガベージコレクションを取得しない場合でも、ファイナライザーは終了時に呼び出されます。

weakrefベースのファイナライザーの他の利点は、モジュールがアンロードされたときにコードを実行するなど、定義がサードパーティによって制御されるクラスのファイナライザーを登録するために使用できることです。

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

ノート

プログラムの終了時にデーモンスレッドでファイナライザーオブジェクトを作成すると、終了時にファイナライザーが呼び出されない可能性があります。 ただし、デーモンスレッド atexit.register()では、try: ... finally: ...およびwith: ...もクリーンアップが発生することを保証しません。