【问题标题】:What exactly is __weakref__ in Python?Python 中的 __weakref__ 到底是什么?
【发布时间】:2016-08-15 17:13:03
【问题描述】:

令人惊讶的是,__weakref__ 没有明确的文档。弱引用解释here__weakref____slots__ 的文档中也有简短提及。但我找不到任何关于 __weakref__ 本身的信息。

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

【问题讨论】:

    标签: python python-3.x python-internals


    【解决方案1】:

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

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

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

    有关此对象的结构和 C-API,请参见 weakrefobject.h。并且实现细节是here

    【讨论】:

      【解决方案2】:

      有趣的是,language documentation 在这个话题上有点缺乏启发性:

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

      C API documentation 更有用:

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

      弱引用形成一个堆栈。该堆栈的顶部(对对象的最新弱引用)可通过__weakref__ 获得。 Weakrefs 尽可能重复使用,因此堆栈通常为空或包含单个元素。

      示例

      当您第一次使用weakref.ref() 时,您会为目标对象创建一个新的弱引用堆栈。此堆栈的顶部是新的弱引用,并存储在目标对象的__weakref__

      >>> import weakref
      >>> class A: pass
      ...
      >>> a = A()
      >>> b = weakref.ref(a)
      >>> c = weakref.ref(a)
      >>> c is b, b is a.__weakref__
      True, True
      >>> weakref.getweakrefs(a)
      [<weakref at 0x10dbe5270; to 'A' at 0x10dbc2fd0>]
      

      如您所见,c 重复使用了b。您可以通过传递回调参数来强制 Python 创建新的弱引用:

      >>> import weakref
      >>> class A: pass
      ...
      >>> def callback(ref): pass
      ...
      >>> a = A()
      >>> b = weakref.ref(a)
      >>> c = weakref.ref(a, callback)
      >>> c is b, b is a.__weakref__
      False, True
      >>> weakref.getweakrefs(a)
      [<weakref at 0x10dbcfcc0; to 'A' at 0x10dbc2fd0>,
       <weakref at 0x10dbe5270; to 'A' at 0x10dbc2fd0>]
      

      现在c 是堆栈中的新弱引用。

      【讨论】:

      • 好的...所以obj.__weakref__ 记住了指向obj 的弱引用wr 并且当obj 被垃圾回收时将用于使wr 无效?
      • @Michael 垃圾收集器在收集 reachable 对象时完全忽略了weakrefs。从垃圾收集器的角度来看,weakref 根本不算作引用。仅当cleaning up 时才考虑弱引用。 GC 遍历weakrefs 列表,使它们无效并调用weakref 上的任何回调。
      【解决方案3】:

      在我们讨论__weakref__ 是什么之前,我们是否需要定义弱引用的概念。

      在大多数文本和资源中,弱引用通常被定义为不保护被引用对象(被引用对象)不被垃圾收集器收集的引用。

      但究竟什么是垃圾回收?

      垃圾收集只是在任何引用/指针不再使用/到达时释放内存的过程。 Python 通过称为引用计数和用于检测和中断引用循环的循环垃圾收集器)的技术执行垃圾收集。使用引用计数,当对象的引用数为 0 时,GC 会在对象变得不可访问时立即收集它们。(更多信息请阅读 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types

      现在,弱引用执行不保护对象不被 GC 收集的任务的方式,或者更好地说,它们导致对象被 GC 收集的方式是这(在使用引用计数而不是跟踪技术的 GC 的情况下)它们不会被计为引用。否则,如果被计入,它们将被称为强引用。

      现在,当我们在 Python 中定义对象时,由于默认情况下所有引用都是强引用,为了创建对对象的弱引用,您需要使用 weakref 模块。每当您创建对对象的弱引用时,该引用都可以通过 __weakref__ 属性访问。

      这是一个例子:

      In [51]: class A:
          ...:     def sample_method(self):
          ...:         return "I'm a sample method"
          ...: 
          ...: 
      
      In [52]: a = A()
      
      In [53]: a.__weakref__
      
      In [54]: # There's no weak reference defined
      
      In [55]: import weakref
      
      In [56]: r1 = weakref.ref(a)
      
      In [57]: a.__weakref__
      Out[57]: <weakref at 0x7f515b7583b0; to 'A' at 0x7f51680f4ee0>
      
      In [58]: r1 is a.__weakref__
      Out[58]: True
      
      # As you can see they both point to one object 
      # which means that a.__weakref__ is precisely r1
      # Also, using weakref.ref always one reference to 
      # the object will be created (singleton)
      
      In [60]: r2 = weakref.ref(a)
      In [62]: r1 is r2
      Out[62]: True
      

      【讨论】:

      • 我在引号文档中没有看到任何关于 __weakref__ 实际作用的内容(它包含弱引用单例)。想补充一下吗?
      • @dhke:这里也一样...弱引用的概念很清楚,我很想知道__weakref__的确切含义和作用。我怀疑它可能是如此微不足道,他们只是省略了它......
      • 考虑到您的编辑和帖子中的最后一句话:__weakref__ 只是一个标志,上面写着:“是的,我可以被弱引用。” (?)
      • @Michael 是的,我认为添加一个示例会更好地描述它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-05
      相关资源
      最近更新 更多