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>