令人惊讶的是,没有明确的文档__weakref__
。弱引用在这里解释。__weakref__
的文档中也简短提及__slots__
。但是我找不到任何关于__weakref__
自己的东西。
到底是__weakref__
什么?-它只是充当标志的成员:如果存在,则该对象可能被弱引用?-还是可以重写/分配以获得所需行为的函数/变量?怎么样?
Answers:
__weakref__
只是一个不透明的对象,它引用了当前对象的所有弱引用。实际上,它是weakref
(或有时是weakproxy
)的实例,它既是对该对象的弱引用,又是对该对象的所有弱引用的双向链接列表的一部分。
这只是一个实现细节,它使垃圾收集器可以通知弱引用其引用对象已被收集,并且不再允许访问其基础指针。
弱引用不能依赖于检查它所引用对象的引用计数。这是因为该内存可能已被回收,并且正在被另一个对象使用。最好的情况是VM崩溃,最坏的情况是弱引用将允许访问其最初未引用的对象。这就是为什么垃圾收集器必须通知弱引用其引用不再有效的原因。
有关该结构的信息,请参见weakrefobject.h,有关此对象的信息,请参见C-API。实现细节在这里
[编辑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__
可以简明地解释其作用,因此可能不应该依赖此行为,因为它是一个实现细节。
的 __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>
__weakref__
实际功能的任何信息(它包含弱参考单例)。愿意添加吗?
__weakref__
。我怀疑这可能太琐碎了,他们只是略去了……
__weakref__
只是一个标语,上面写着:“是的,我可以被弱引用。” (?)
obj.__weakref__
请记住wr
指向的弱引用将在垃圾回收何时obj
无效?wr
obj