令人惊讶的是,__weakref__
没有明确的文档。解释了弱引用here。 __weakref__
的文档中也很快提到了__slots__
。但是我找不到关于__weakref__
本身的任何内容。
究竟是什么__weakref__
?
- 它只是一个作为标志的成员:如果存在,该对象可能被弱引用?
- 或者它是一个可以被覆盖/分配以获得所需行为的函数/变量?怎么样?
答案 0 :(得分:41)
__weakref__
只是一个不透明的对象,它引用了对当前对象的所有弱引用。实际上它是weakref
(或有时weakproxy
)的一个实例,它既是对象的弱引用,也是对该对象的所有弱引用的双向链表的一部分。
它只是一个实现细节,它允许垃圾收集器通知弱引用它已经收集了它的引用,并且不再允许访问它的底层指针。
弱引用不能依赖于检查它引用的对象的引用计数。这是因为该内存可能已被回收,现在正由另一个对象使用。最好的情况是VM会崩溃,最坏的情况是弱引用将允许访问它最初没有引用的对象。这就是为什么垃圾收集器必须通知弱引用它的引用不再有效。
有关此对象的结构和C-API,请参阅weakrefobject.h。实施细节为here
答案 1 :(得分:25)
[编辑1:解释链表性质以及重复使用弱点时]
有趣的是,official documentation在这个主题上有点不具启发性:
对于每个实例没有
__weakref__
变量,定义__slots__
的类不支持对其实例的弱引用。如果需要弱引用支持,则将__weakref__
添加到__slots__
声明中的字符串序列。
关于该主题的type
object documentation似乎没有太多帮助:
当类型的
__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
是链中的第二个引用。 Python代码无法直接访问引用链。我们只看到链的头部元素(b
),但不知道链是如何继续的(b
- > c
)。
所以__weakref__
是对象的所有弱引用的内部链表的头部。我找不到任何官方文档,其中__weakref__
的这个角色被简明地解释,所以应该不要依赖于这种行为,因为它是一个实现细节。
答案 2 :(得分:16)
__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__
变量的实例创建弱引用并不是&#39;提出TypeError
:
>>> class Test:
... __slots__ = ['a', 'b']
...
>>> t = Test()
>>>
>>> r = weakref.ref(t)
>>>
>>> r
<weakref at 0x7fe49f4185d0; to 'instance' at 0x7fe4a3e75f80>