Python中的__weakref__到底是什么?

What exactly is __weakref__ in Python?

令人惊讶的是,__weakref__ 没有明确的文档。解释了弱引用 here__weakref____slots__ 的文档中也有简短提及。但是我找不到关于 __weakref__ 本身的任何信息。

__weakref__到底是什么? - 它只是一个充当标志的成员:如果存在,该对象可能是弱引用的? - 还是 function/variable 可以 overridden/assigned 以获得所需的行为?怎么样?

在我们讨论什么是 __weakref__ 之前,我们需要定义弱引用的概念。

在大多数文本和来源中,弱引用通常被定义为不保护引用对象(引用对象)不被垃圾收集器收集的引用。

但是垃圾收集到底是什么?

垃圾收集只是当内存不再被任何 reference/pointer used/reached 时释放内存的过程。 Python 通过称为 引用计数 的技术执行垃圾收集( 以及用于检测和中断引用循环的循环垃圾收集器 ) .使用 引用计数 ,当对象的引用数为 0 时,GC 会在对象变得不可访问时立即收集对象。(有关更多信息,请阅读 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types

现在,弱引用执行不保护对象不被 GC 收集的任务的方式,或者更确切地说是它们导致对象被回收的方式被 GC 收集的是(在使用引用计数而不是跟踪技术的 GC 的情况下)它们不会被计为 reference.Otherwise,如果被计入,它们将被称为强引用。

现在,当我们在 Python 中定义对象时,由于默认情况下所有引用都是强引用,为了创建对象的弱引用,您需要使用 weakref 模块。每当您创建对对象的弱引用时,该引用都可以通过 __weakref__ 属性访问。

这是一个例子:

In [51]: class A:
    ...:     def sample_method(self):
    ...:         return "I'm a sample method"
    ...: 
    ...: 

In [52]: a = A()

In [53]: a.__weakref__

In [54]: # There's no weak reference defined

In [55]: import weakref

In [56]: r1 = weakref.ref(a)

In [57]: a.__weakref__
Out[57]: <weakref at 0x7f515b7583b0; to 'A' at 0x7f51680f4ee0>

In [58]: r1 is a.__weakref__
Out[58]: True

# As you can see they both point to one object 
# which means that a.__weakref__ is precisely r1
# Also, using weakref.ref always one reference to 
# the object will be created (singleton)

In [60]: r2 = weakref.ref(a)
In [62]: r1 is r2
Out[62]: True

有趣的是,language documentation 在这个话题上有点没有启发性:

Without a __weakref__ variable for each instance, classes defining __slots__ do not support weak references to its instances. If weak reference support is needed, then add '__weakref__' to the sequence of strings in the __slots__ declaration.

C API documentation更有用:

When a type’s __slots__ declaration contains a slot named __weakref__, that slot becomes the weak reference list head for instances of the type, and the slot’s offset is stored in the type’s tp_weaklistoffset.

弱引用形成堆栈。该堆栈的顶部(对对象的最新弱引用)可通过 __weakref__ 获得。 Weakrefs 会尽可能重复使用,因此堆栈通常为空或包含单个元素。

例子

当你第一次使用weakref.ref()时,你为目标对象创建了一个新的弱引用栈。此堆栈的顶部是新的弱引用,并存储在目标对象的 __weakref__:

>>> import weakref
>>> class A: pass
...
>>> a = A()
>>> b = weakref.ref(a)
>>> c = weakref.ref(a)
>>> c is b, b is a.__weakref__
True, True
>>> weakref.getweakrefs(a)
[<weakref at 0x10dbe5270; to 'A' at 0x10dbc2fd0>]

如您所见,c 重复使用了 b。您可以通过传递回调参数强制 Python 创建一个新的弱引用:

>>> import weakref
>>> class A: pass
...
>>> def callback(ref): pass
...
>>> a = A()
>>> b = weakref.ref(a)
>>> c = weakref.ref(a, callback)
>>> c is b, b is a.__weakref__
False, True
>>> weakref.getweakrefs(a)
[<weakref at 0x10dbcfcc0; to 'A' at 0x10dbc2fd0>,
 <weakref at 0x10dbe5270; to 'A' at 0x10dbc2fd0>]

现在 c 是堆栈中的新弱引用。

__weakref__只是一个不透明的对象,引用了当前对象的所有弱引用。事实上,它是 weakref(或有时 weakproxy)的一个实例,它既是对该对象的弱引用,也是该对象所有弱引用的双向链表的一部分。

这只是一个实现细节,允许垃圾收集器通知弱引用其引用已被收集,并且不再允许访问其底层指针。

弱引用不能依赖于检查它引用的对象的引用计数。这是因为该内存可能已被回收并且现在正被另一个对象使用。最好的情况是 VM 会崩溃,最坏的情况是弱引用将允许访问它最初未引用的对象。这就是垃圾收集器必须通知弱引用其引用不再有效的原因。

weakrefobject.h for the structure and C-API for this object. And the implementation detail is here