驚いたことに、__weakref__
の明示的なドキュメントはありません。弱い参照は こちら で説明されています。 __weakref__
は、__slots__
のドキュメントでも簡単に言及されています。しかし、__weakref__
自体については何も見つかりませんでした。
__weakref__
とは正確には何ですか? -それはフラグとして機能する単なるメンバーですか:存在する場合、オブジェクトは弱参照される可能性がありますか? -それとも、目的の動作を得るためにオーバーライド/割り当てできる関数/変数ですか?どうやって?
__weakref__
は、現在のオブジェクトへのすべての弱い参照を参照する不透明なオブジェクトです。実際、それはweakref
(またはweakproxy
)のインスタンスであり、オブジェクトへの弱参照であり、そのオブジェクトのすべての弱参照への二重リンクリストの一部です。
これは、ガベージコレクターが参照先が収集されたことを弱参照に通知し、基になるポインターへのアクセスを許可しないようにする実装の詳細です。
弱参照は、参照するオブジェクトの参照カウントの確認に依存することはできません。これは、そのメモリが回収され、現在別のオブジェクトによって使用されているためです。最良のシナリオVMはクラッシュし、最悪の場合、弱参照は元々参照していなかったオブジェクトへのアクセスを許可します。これが、ガベージコレクタがその参照先である弱参照に通知する必要がある理由です。もう有効ではない。
このオブジェクトの構造とC-APIについては weakrefobject.h をご覧ください。実装の詳細は here です
[編集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__
_のこの役割が簡潔に説明されている公式ドキュメントは見つかりません。したがって、実装の詳細であるため、おそらくこの動作に依存しないでください。
__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>