Python中的__weakref__究竟是什么?

令人惊讶的是, __weakref__没有明确的文档。 在这里解释弱引用。 __weakref____slots__的文档中也很快提到。 但是我找不到关于__weakref__本身的任何内容。

什么是__weakref__ ? – 它只是一个作为旗帜的成员:如果存在,对象可能被弱引用? – 或者它是一个函数/variables,可以被覆盖/分配来获得所需的行为? 怎么样?

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

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

弱引用不能依赖于检查它引用的对象的引用计数。这是因为内存可能已经被回收并且现在被另一个对象使用。 最好的情况下虚拟机会崩溃,最坏的情况下,弱引用将允许访问它最初并没有提到的对象。 这就是为什么垃圾收集器必须通知弱引用它的引用不再有效。

请参阅weakrefobject.h以获取此对象的结构和C-API。 这里的实现细节

[编辑1:解释链表的性质以及何时重新使用弱参数]

有趣的是, 官方文件在这个话题上有些不起作用:

没有每个实例的__weakref__variables,定义__slots__类不支持对其实例的弱引用。 如果需要弱引用支持,则将__weakref__添加到__slots__声明中的string序列。

关于该主题的type对象文档似乎并没有太多的帮助:

当一个types的__slots__声明包含一个名为__weakref__插槽时,该插槽将成为该types实例的弱引用列表头,并且该插槽的偏移量将存储在该types的tp_weaklistoffset

弱引用形成一个链表。 该列表的头部(对象的第一个弱引用)可以通过__weakref__ 。 Weakref被重用,只要有可能,所以列表(不是Python列表!)通常是空的或包含一个单一的元素。

例如

当你第一次使用weakref.ref() ,你为目标对象创build一个新的弱引用链。 这个链的头部是新的weakref,并存储在目标对象的__weakref__

 >>> a = A() >>> b = weakref.ref(a) >>> c = weakref.ref(b) >>> print(b is c is a.__weakref__) True 

我们可以看到, b被重新使用。 我们可以强制python创build一个新的weakref,例如添加一个callback参数:

 >>> 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是链中的第二个参考。 引用链不能直接从Python代码访问。 我们只看到链( b )的头部元素,但不知道链如何继续( b – > c )。

所以__weakref__是所有对象的弱引用的内部链表的头部。 我找不到任何官方文档,其中__weakref__这个angular色被简洁地解释了,所以可能不应该依赖这个行为,因为它是一个实现细节。

__weakref__variables是一个属性,它使对象能够支持弱引用并保留对象的弱引用。

正如你所提到的python文档已经在这里解释weakref

当只剩下引用的引用是弱引用时,垃圾收集可以自由地销毁指代对象,并将其内存用于别的东西。

因此,弱引用的职责是为一个对象提供条件,以便能够被垃圾收集,而不pipe它的types和范围。

关于__slots__文档解释得非常好:

默认情况下,类的实例具有属性存储的字典。 这浪费了具有很less实例variables的对象的空间。 创build大量实例时,空间消耗可能变得尖锐。

可以通过在类定义中定义__slots__来覆盖默认值。 __slots__声明采用一系列实例variables,并在每个实例中保留足够的空间来为每个variables保存一个值。 保存空间是因为__dict__不是为每个实例创build的。

因此,通过使用__slots__您将控制您的属性所需的存储空间,实际上可以防止为每个实例自动创build__dict____weakref__ 。 其中__weakref__是每个对象的必要variables,以便能够处理弱引用。

正如object.__slots__类的文档所说:

这个类variables可以被赋予一个string,可迭代的或者由实例使用的具有variables名称的string序列。 __slots__为声明的variables保留空间,并防止为每个实例自动创build__dict____weakref__

简而言之__weakref__是用于手动pipe理存储分配的,因为__weakref__是接受与存储有关的对象的弱引用的许可(因为被垃圾收集的能力),所以__slots__将控制__weakref__以及控制__dict__属性。

另外,文档向您展示了如何使用__slots__来支持弱引用。

没有每个实例的__weakref__variables,定义__slots__类不支持对其实例的弱引用。 如果需要弱引用支持,则将'__weakref__'添加到__slots__声明中的string序列中。

这里是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中,尽pipe文档类似于上述文档, __weakref____slots__名称中没有提供__weakref__variables的实例创build弱引用不会引发TypeError

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