web-dev-qa-db-ja.com

Pythonの__weakref__とは正確には何ですか?

驚いたことに、__weakref__の明示的なドキュメントはありません。弱い参照は こちら で説明されています。 __weakref__は、__slots__のドキュメントでも簡単に言及されています。しかし、__weakref__自体については何も見つかりませんでした。

__weakref__とは正確には何ですか? -それはフラグとして機能する単なるメンバーですか:存在する場合、オブジェクトは弱参照される可能性がありますか? -それとも、目的の動作を得るためにオーバーライド/割り当てできる関数/変数ですか?どうやって?

55
Michael

__weakref__は、現在のオブジェクトへのすべての弱い参照を参照する不透明なオブジェクトです。実際、それはweakref(またはweakproxy)のインスタンスであり、オブジェクトへの弱参照であり、そのオブジェクトのすべての弱参照への二重リンクリストの一部です。

これは、ガベージコレクターが参照先が収集されたことを弱参照に通知し、基になるポインターへのアクセスを許可しないようにする実装の詳細です。

弱参照は、参照するオブジェクトの参照カウントの確認に依存することはできません。これは、そのメモリが回収され、現在別のオブジェクトによって使用されているためです。最良のシナリオVMはクラッシュし、最悪の場合、弱参照は元々参照していなかったオブジェクトへのアクセスを許可します。これが、ガベージコレクタがその参照先である弱参照に通知する必要がある理由です。もう有効ではない。

このオブジェクトの構造とC-APIについては weakrefobject.h をご覧ください。実装の詳細は here です

43
Dunes

[編集1:リンクリストの性質とweakrefが再利用される場合の説明]

興味深いことに、 公式ドキュメント は、このトピックに関してやや啓発的ではありません。

各インスタンスに___weakref___変数がない場合、___slots___を定義するクラスは、そのインスタンスへの弱参照をサポートしません。弱参照のサポートが必要な場合は、___weakref___宣言内の文字列のシーケンスに___slots___を追加します。

トピックの typeオブジェクトのドキュメント は、物事をあまり助けていないようです:

型の___slots___宣言に___weakref___という名前のスロットが含まれている場合、そのスロットはその型のインスタンスの弱参照リストの先頭になり、スロットのオフセットは型の_tp_weaklistoffset_に格納されます。

弱い参照はリンクリストを形成します。そのリストの先頭(オブジェクトへの最初の弱い参照)は、___weakref___を介して利用できます。 Weakrefsは可能な限り再利用されるため、リスト(Pythonリスト!)ではありません)は通常、空であるか、単一の要素を含んでいます。

最初にweakref.ref()を使用するとき、ターゲットオブジェクトの新しい弱参照チェーンを作成します。このチェーンの先頭は新しいweakrefであり、ターゲットオブジェクトの___weakref___に格納されます。

_>>> import weakref
>>> class A(object): pass
>>> a = A()
>>> b = weakref.ref(a)
>>> c = weakref.ref(b)
>>> print(b is c is a.__weakref__)
True
_

ご覧のとおり、bが再利用されています。 pythonを強制的に新しいweakrefを作成するには、コールバックパラメーターを追加します。

_>>> def callback():
>>>   pass
>>> a = A()
>>> b = weakref.ref(a)
>>> c = weakref.ref(b, callback)
>>> print(b is c is a.__weakref__)
False
_

これで_b is a.__weakref___となり、cがチェーンの2番目の参照になります。参照チェーンには、Pythonコードから直接アクセスできません。チェーンの先頭要素(b)のみが表示されますが、チェーンの継続方法(b-> c)は表示されません。

したがって、___weakref___は、オブジェクトへのすべての弱参照の内部リンクリストの先頭です。 ___weakref___のこの役割が簡潔に説明されている公式ドキュメントは見つかりません。したがって、実装の詳細であるため、おそらくこの動作に依存しないでください。

26
dhke

__weakref__変数は、オブジェクトが弱参照をサポートし、オブジェクトへの弱参照を保持するようにする属性です。

pythonのドキュメントでは、次のように説明されています。

参照先への残りの参照が弱い参照のみである場合、ガベージコレクションは参照先を自由に破棄し、そのメモリを他の何かに再利用できます。

したがって、弱参照の義務は、オブジェクトのタイプとスコープに関係なくガベージコレクションできるように、オブジェクトの条件を提供することです。

また、__slots__については、最初にドキュメントを確認できます。

デフォルトでは、クラスのインスタンスには属性ストレージ用の辞書があります。これにより、インスタンス変数が非常に少ないオブジェクトのスペースが無駄になります。大量のインスタンスを作成すると、スペースの消費が急激になる場合があります。

デフォルトは、クラス定義で__slots__を定義することでオーバーライドできます。 __slots__宣言は、一連のインスタンス変数を取り、各インスタンスに十分なスペースを確保して、各変数の値を保持します。 __dict__はインスタンスごとに作成されないため、スペースが節約されます。

これで、__slots__を使用することで、属性に必要なストレージを制御できるため、各インスタンスに対して__dict__および__weakref__が自動的に作成されなくなります。 __weakref__は、弱参照に対処するために各オブジェクトの必要な変数です。

また、これらすべてに加えて、object.__slots__クラスのドキュメントには次のように記載されています。

このクラス変数には、インスタンスで使用される変数名を持つ文字列、反復可能、または文字列のシーケンスを割り当てることができます。 __slots__は、宣言された変数用のスペースを予約し、各インスタンスの__dict__および__weakref__の自動作成を防ぎます。

つまり、__slots__はストレージの割り当てを手動で管理するためのものであり、__weakref__はストレージに関連するオブジェクトの弱参照を受け入れるライセンスであると結論付けることができます(したがって、__slots____weakref__を制御し、__dict__属性を制御します。

また、ドキュメントでは、__slots__の使用と並行して、弱参照をサポートするオブジェクトを作成する方法が示されています。

各インスタンスに__weakref__変数がない場合、__slots__を定義するクラスは、そのインスタンスへの弱参照をサポートしません。弱参照のサポートが必要な場合は、'__weakref__'宣言の文字列のシーケンスに__slots__を追加します。

python 3.Xの例を次に示します。

>>> class Test:
...     __slots__ = ['a', 'b']
... 
>>> 
>>> import weakref
>>> 
>>> t = Test()
>>> 
>>> r = weakref.ref(t)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot create weak reference to 'Test' object
>>> 
>>> class Test:
...     __slots__ = ['a', 'b', '__weakref__']
... 
>>> t = Test()
>>> r = weakref.ref(t)
>>> 
>>> t.__weakref__
<weakref at 0x7f735bc55d68; to 'Test' at 0x7f735bc51fc8>

ただし、python 2.7では、ドキュメントは前述のドキュメントに似ていますが、__weakref__変数に__slots__変数を提供しないインスタンスから弱い参照を作成しています] TypeErrorを発生させません:

>>> class Test:
...    __slots__ = ['a', 'b']
... 
>>> t = Test()
>>> 
>>> r = weakref.ref(t)
>>> 
>>> r
<weakref at 0x7fe49f4185d0; to 'instance' at 0x7fe4a3e75f80>
16
Kasrâmvd