Python中的__weakref__到底是什么?


70

令人惊讶的是,没有明确的文档__weakref__。弱引用在这里解释。__weakref__的文档中也简短提及__slots__。但是我找不到任何关于__weakref__自己的东西。

到底是__weakref__什么?-它只是充当标志的成员:如果存在,则该对象可能被弱引用?-还是可以重写/分配以获得所需行为的函数/变量?怎么样?

Answers:


57

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

这只是一个实现细节,它使垃圾收集器可以通知弱引用其引用对象已被收集,并且不再允许访问其基础指针。

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

有关该结构的信息,请参见weakrefobject.h,有关此对象的信息,请参见C-API。实现细节在这里


38

[编辑1:说明链表的性质以及弱引用的重用时间]

有趣的是,官方文档对该主题没有启发性:

__weakref__对于每个实例,如果没有变量,则定义的类__slots__不支持对其实例的弱引用。如果需要弱引用支持,则__weakref____slots__声明中添加到字符串序列。

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

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

弱引用形成一个链表。该列表的开头(对对象的第一个弱引用)可通过访问__weakref__。弱引用会在可能的情况下重新使用,因此列表(不是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代码直接访问参考链。我们仅看到链的head元素(b),而没有看到链如何继续(b-> c)。

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


好的...因此,obj.__weakref__请记住wr指向的弱引用将在垃圾回收何时obj无效?wrobj
迈克尔

1
@Michael垃圾收集器在收集可到达的对象时完全忽略了弱引用。从垃圾收集器的角度来看,weakref根本不算作参考。弱引用仅在清理时才考虑。GC遍历弱引用列表,使它们无效,并调用弱引用上的任何回调。
dhke '16

18

__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>

2
我在报价单文档中看不到有关__weakref__实际功能的任何信息(它包含弱参考单例)。愿意添加吗?
dhke '16

1
@dhke:这里也一样……弱引用的概念很明确,我真的很想知道的确切含义和功能__weakref__。我怀疑这可能太琐碎了,他们只是略去了……
Michael

考虑到您的编辑和文章的最后一句话:__weakref__只是一个标语,上面写着:“是的,我可以被弱引用。” (?)
Michael

@Michael是的,我认为添加一个示例会更好地描述它。
Kasravnd '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.