Python中的__weakref__究竟是什么?

Mic*_*ael 57 python python-3.x python-internals

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

到底是__weakref__什么? - 它只是一个作为标志的成员:如果存在,该对象可能被弱引用? - 或者它是一个可以被覆盖/分配以获得所需行为的函数/变量?怎么样?

Dun*_*nes 45

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

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

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

有关此对象的结构和C-API,请参阅weakrefobject.h.实现细节在这里


dhk*_*hke 28

[编辑1:解释链表性质以及重复使用弱化时]

有趣的是,官方文档在这个主题上有点不具启发性:

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

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

当类型的__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
Run Code Online (Sandbox Code Playgroud)

我们可以看到,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
Run Code Online (Sandbox Code Playgroud)

现在b is a.__weakref__,它c是链中的第二个参考.Python代码无法直接访问引用链.我们只看到chain(b)的head元素,但不知道链如何继续(b- > c).

__weakref__所有对象的弱引用的内部链表的头部也是如此.我找不到任何官方文档,其中__weakref__简要解释了这个角色,因此可能不应该依赖于这种行为,因为它是一个实现细节.


Kas*_*mvd 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>
Run Code Online (Sandbox Code Playgroud)

但是在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>
Run Code Online (Sandbox Code Playgroud)

  • 我没有在引号文档中看到有关`__weakref__`实际上做什么的内容(它包含弱引用单例).小心补充一点? (2认同)