8.8. "weakref" --- 弱引用
*************************

**源码：** Lib/weakref.py

======================================================================

"weakref" 模块允许Python程序员创建对象的 *weak references* 。

在下文中，术语 *referent* 表示由弱引用引用的对象。

对对象的弱引用不能保证对象存活：当对像的引用只剩弱引用时， *garbage
collection* 可以销毁引用并将其内存重用于其他内容。但是，在实际销毁对象
之前，即使没有强引用，弱引用也一直能返回该对象。

弱引用的主要用途是实现保存大对象的高速缓存或映射，但又不希望大对象仅仅
因为它出现在高速缓存或映射中而保持存活。

例如，如果您有许多大型二进制图像对象，则可能希望将名称与每个对象关联起
来。如果您使用Python字典将名称映射到图像，或将图像映射到名称，则图像对
象将保持活动状态，因为它们在字典中显示为值或键。 "weakref" 模块提供的
"WeakKeyDictionary" 和 "WeakValueDictionary" 类可以替代Python字典，使
用弱引用来构造映射，这些映射不会仅仅因为它们出现在映射对象中而使对象保
持存活。例如，如果一个图像对象是 "WeakValueDictionary" 中的值，那么当
对该图像对象的剩余引用是弱映射对象所持有的弱引用时，垃圾回收可以回收该
对象并将其在弱映射对象中相应的条目删除。

"WeakKeyDictionary" 和 "WeakValueDictionary" 在它们的实现中使用弱引用
，在弱引用上设置回调函数，当键或值被垃圾回收回收时通知弱字典。
"WeakSet" 实现了 "set" 接口，但像 "WeakKeyDictionary" 一样，只持有其元
素的弱引用。

"finalize" 提供了注册一个对象被垃圾收集时要调用的清理函数的方式。这比
在原始弱引用上设置回调函数更简单，因为模块会自动确保对象被回收前终结器
一直保持存活。

这些弱容器类型之一或者 "finalize" 就是大多数程序所需要的 - 通常不需要
直接创建自己的弱引用。"weakref" 模块暴露了低级机制，以便于高级用途。

Not all objects can be weakly referenced; those objects which can
include class instances, functions written in Python (but not in C),
instance methods, sets, frozensets, some *file objects*, *generator*s,
type objects, sockets, arrays, deques, regular expression pattern
objects, and code objects.

在 3.2 版更改: 添加了对thread.lock，threading.Lock和代码对象的支持。

几个内建类型如 "list" 和 "dict" 不直接支持弱引用，但可以通过子类化添加
支持:

   class Dict(dict):
       pass

   obj = Dict(red=1, green=2, blue=3)   # this object is weak referenceable

Other built-in types such as "tuple" and "int" do not support weak
references even when subclassed (This is an implementation detail and
may be different across various Python implementations.).

Extension types can easily be made to support weak references; see
Weak Reference Support.

class weakref.ref(object[, callback])

   返回对 *对象* 的弱引用。如果原始对象仍然存活，则可以通过调用引用对
   象来检索原始对象；如果引用的原始对象不再存在，则调用引用对象将得到
   "None" 。如果提供了 *回调* 而且值不是 "None" ，并且返回的弱引用对象
   仍然存活，则在对象即将终结时将调用回调;弱引用对象将作为回调的唯一参
   数传递；指示物将不再可用。

   许多弱引用也允许针对相同对象来构建。 为每个弱引用注册的回调将按从最
   近注册的回调到最早注册的回调的顺序被调用。

   回调所引发的异常将记录于标准错误输出，但无法被传播；它们会按与对象
   的 "__del__()" 方法所引发的异常相同的方式被处理。

   如果 *object* 可哈希，则弱引用也为 *hashable*。 即使在 *object* 被
   删除之后它们仍将保持其哈希值。 如果 "hash()" 在 *object* 被删除之后
   才首次被调用，则该调用将引发 "TypeError"。

   弱引用支持相等检测，但不支持排序比较。 如果被引用对象仍然存在，两个
   引用具有与它们的被引用对象一致的相等关系（无论 *callback* 是否相同
   ）。 如果删除了任一被引用对象，则仅在两个引用对象为同一对象时两者才
   相等。

   这是一个可子类化的类型而非一个工厂函数。

   __callback__

      这个只读属性会返回当前关联到弱引用的回调。 如果回调不存在或弱引
      用的被引用对象已不存在，则此属性的值为 "None"。

   在 3.4 版更改: 添加了 "__callback__" 属性。

weakref.proxy(object[, callback])

   返回 *object* 的一个使用弱引用的代理。 此函数支持在大多数上下文中使
   用代理，而不要求显式地对所使用的弱引用对象解除引用。 返回的对象类型
   将为 "ProxyType" 或 "CallableProxyType"，具体取决于 *object* 是否可
   调用。 Proxy 对象不是 *hashable* 对象，无论被引用对象是否可哈希；这
   可避免与它们的基本可变性质相关的多种问题，并可防止它们被用作字典键
   。 *callback* 与 "ref()" 函数的同名形参含义相同。

weakref.getweakrefcount(object)

   返回指向 *object* 的弱引用和代理的数量。

weakref.getweakrefs(object)

   返回由指向 *object* 的所有弱引用和代理构成的列表。

class weakref.WeakKeyDictionary([dict])

   弱引用键的映射类。 当不再有对键的强引用时字典中的条目将被丢弃。 这
   可被用来将额外数据关联到一个应用中其他部分所拥有的对象而无需在那些
   对象中添加属性。 这对于重载了属性访问的对象来说特别有用。

   注解:

     Caution: Because a "WeakKeyDictionary" is built on top of a
     Python dictionary, it must not change size when iterating over
     it.  This can be difficult to ensure for a "WeakKeyDictionary"
     because actions performed by the program during iteration may
     cause items in the dictionary to vanish "by magic" (as a side
     effect of garbage collection).

"WeakKeyDictionary" 对象具有一个额外方法可以直接公开内部引用。 这些引
用不保证在它们被使用时仍然保持“存活”，因此这些引用的调用结果需要在使用
前进行检测。 此方法可用于避免创建会导致垃圾回收器将保留键超出实际需要
时长的引用。

WeakKeyDictionary.keyrefs()

   返回包含对键的弱引用的可迭代对象。

class weakref.WeakValueDictionary([dict])

   弱引用值的映射类。 当不再有对键的强引用时字典中的条目将被丢弃。

   注解:

     Caution:  Because a "WeakValueDictionary" is built on top of a
     Python dictionary, it must not change size when iterating over
     it.  This can be difficult to ensure for a "WeakValueDictionary"
     because actions performed by the program during iteration may
     cause items in the dictionary to vanish "by magic" (as a side
     effect of garbage collection).

"WeakValueDictionary" 对象具有一个额外方法，此方法存在与
"WeakKeyDictionary" 对象的 "keyrefs()" 方法相同的问题。

WeakValueDictionary.valuerefs()

   返回包含对值的弱引用的可迭代对象。

class weakref.WeakSet([elements])

   保持对其元素弱引用的集合类。 当不再有对某个元素的强引用时元素将被丢
   弃。

class weakref.WeakMethod(method)

   一个模拟对绑定方法（即在类中定义并在实例中查找的方法）进行弱引用的
   自定义 "ref" 子类。 由于绑定方法是临时性的，标准弱引用无法保持它。
   "WeakMethod" 包含特别代码用来重新创建绑定方法，直到对象或初始函数被
   销毁:

      >>> class C:
      ...     def method(self):
      ...         print("method called!")
      ...
      >>> c = C()
      >>> r = weakref.ref(c.method)
      >>> r()
      >>> r = weakref.WeakMethod(c.method)
      >>> r()
      <bound method C.method of <__main__.C object at 0x7fc859830220>>
      >>> r()()
      method called!
      >>> del c
      >>> gc.collect()
      0
      >>> r()
      >>>

   3.4 新版功能.

class weakref.finalize(obj, func, *args, **kwargs)

   返回一个可调用的终结器对象，该对象将在 *obj* 作为垃圾回收时被调用。
   与普通的弱引用不同，终结器将总是存活，直到引用对象被回收，这极大地
   简化了生存期管理。

   终结器总是被视为 *存活* 直到它被调用（显式调用或在垃圾回收时隐式调
   用），调用之后它将 *死亡*。 调用存活的终结器将返回 "func(*arg,
   **kwargs)" 的求值结果，而调用死亡的终结器将返回 "None"。

   在垃圾收集期间由终结器回调所引发异常将显示于标准错误输出，但无法被
   传播。 它们会按与对象的 "__del__()" 方法或弱引用的回调所引发异常相
   同的方式被处理。

   当程序退出时，剩余的存活终结器会被调用，除非它们的 "atexit" 属性已
   被设为假值。 它们会按与创建时相反的顺序被调用。

   终结器在 *interpreter shutdown* 的后期绝不会发起调用其回调函数，此
   时模块全局变量很可能已被替换为 "None"。

   __call__()

      如果 *self* 为存活状态则将其标记为已死亡，并返回调用
      "func(*args, **kwargs)" 的结果。 如果 *self* 已死亡则返回 "None"
      。

   detach()

      如果 *self* 为存活状态则将其标记为已死亡，并返回元组 "(obj,
      func, args, kwargs)"。 如果 *self* 已死亡则返 "None"。

   peek()

      如果 *self* 为存活状态则返回元组 "(obj, func, args, kwargs)"。
      如果 *self* 已死亡则返回 "None"。

   alive

      如果终结器为存活状态则该特征属性为真值，否则为假值。

   atexit

      一个可写的布尔型特征属性，默认为真值。 当程序退出时，它会调用所
      有 "atexit" 为真值的剩余存活终结器。 它们会按与创建时相反的顺序
      被调用。

   注解:

     很重要的一点是确保 *func*, *args* 和 *kwargs* 不拥有任何对 *obj*
     的引用，无论是直接的或是间接的，否则的话 *obj* 将永远不会被作为垃
     圾回收。 特别地，*func* 不应当是 *obj* 的一个绑定方法。

   3.4 新版功能.

weakref.ReferenceType

   弱引用对象的类型对象。

weakref.ProxyType

   不可调用的对象的代理的类型对象。

weakref.CallableProxyType

   可调用对象的代理的类型对象。

weakref.ProxyTypes

   包含所有代理的类型对象的序列。 这可以用于更方便地检测一个对象是否是
   代理，而不必依赖于两种代理对象的名称。

exception weakref.ReferenceError

   Exception raised when a proxy object is used but the underlying
   object has been collected.  This is the same as the standard
   "ReferenceError" exception.

参见:

  **PEP 205** - 弱引用
     此特性的提议和理由，包括早期实现的链接和其他语言中类似特性的相关
     信息。


8.8.1. 弱引用对象
=================

弱引用对象没有 "ref.__callback__" 以外的方法和属性。 一个弱引用对象如
果存在，就允许通过调用它来获取引用：

>>> import weakref
>>> class Object:
...     pass
...
>>> o = Object()
>>> r = weakref.ref(o)
>>> o2 = r()
>>> o is o2
True

如果引用已不存在，则调用引用对象将返回 "None":

>>> del o, o2
>>> print(r())
None

检测一个弱引用对象是否仍然存在应该使用表达式 "ref() is not None"。 通
常，需要使用引用对象的应用代码应当遵循这样的模式:

   # r is a weak reference object
   o = r()
   if o is None:
       # referent has been garbage collected
       print("Object has been deallocated; can't frobnicate.")
   else:
       print("Object is still live!")
       o.do_something_useful()

使用单独的“存活”测试会在多线程应用中制造竞争条件；其他线程可能导致某个
弱引用在该弱引用被调用前就失效；上述的写法在多线程应用和单线程应用中都
是安全的。

特别版本的 "ref" 对象可以通过子类化来创建。 在 "WeakValueDictionary"
的实现中就使用了这种方式来减少映射中每个条目的内存开销。 这对于将附加
信息关联到引用的情况最为适用，但也可以被用于在调用中插入额外处理来提取
引用。

这个例子演示了如何将 "ref" 的一个子类用于存储有关对象的附加信息并在引
用被访问时影响其所返回的值:

   import weakref

   class ExtendedRef(weakref.ref):
       def __init__(self, ob, callback=None, **annotations):
           super(ExtendedRef, self).__init__(ob, callback)
           self.__counter = 0
           for k, v in annotations.items():
               setattr(self, k, v)

       def __call__(self):
           """Return a pair containing the referent and the number of
           times the reference has been called.
           """
           ob = super(ExtendedRef, self).__call__()
           if ob is not None:
               self.__counter += 1
               ob = (ob, self.__counter)
           return ob


8.8.2. 示例
===========

这个简单的例子演示了一个应用如何使用对象 ID 来提取之前出现过的对象。
然后对象的 ID 可以在其它数据结构中使用，而无须强制对象保持存活，但处于
存活状态的对象也仍然可以通过 ID 来提取。

   import weakref

   _id2obj_dict = weakref.WeakValueDictionary()

   def remember(obj):
       oid = id(obj)
       _id2obj_dict[oid] = obj
       return oid

   def id2obj(oid):
       return _id2obj_dict[oid]


8.8.3. 终结器对象
=================

使用 "finalize" 的主要好处在于它能更简便地注册回调函数，而无须保留所返
回的终结器对象。 例如

>>> import weakref
>>> class Object:
...     pass
...
>>> kenny = Object()
>>> weakref.finalize(kenny, print, "You killed Kenny!")  
<finalize object at ...; for 'Object' at ...>
>>> del kenny
You killed Kenny!

终结器也可以被直接调用。 但是终结器最多只能对回调函数发起一次调用。

>>> def callback(x, y, z):
...     print("CALLBACK")
...     return x + y + z
...
>>> obj = Object()
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
>>> assert f.alive
>>> assert f() == 6
CALLBACK
>>> assert not f.alive
>>> f()                     # callback not called because finalizer dead
>>> del obj                 # callback not called because finalizer dead

你可以使用 "detach()" 方法来注销一个终结器。 该方法将销毁终结器并返回
其被创建时传给构造器的参数。

>>> obj = Object()
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
>>> f.detach()                                           
(<__main__.Object object ...>, <function callback ...>, (1, 2), {'z': 3})
>>> newobj, func, args, kwargs = _
>>> assert not f.alive
>>> assert newobj is obj
>>> assert func(*args, **kwargs) == 6
CALLBACK

除非你将 "atexit" 属性设为 "False"，否则终结器在程序退出时如果仍然存活
就将被调用。 例如

>>> obj = Object()
>>> weakref.finalize(obj, print, "obj dead or exiting")  
<finalize object at ...; for 'Object' at ...>
>>> exit()                                               
obj dead or exiting


8.8.4. 比较终结器与 "__del__()" 方法
====================================

假设我们想创建一个类，用它的实例来代表临时目录。 当以下事件中的某一个
发生时，这个目录应当与其内容一起被删除：

* 对象被作为垃圾回收，

* 对象的 "remove()" 方法被调用，或

* 程序退出。

我们可以尝试使用 "__del__()" 方法来实现这个类，如下所示:

   class TempDir:
       def __init__(self):
           self.name = tempfile.mkdtemp()

       def remove(self):
           if self.name is not None:
               shutil.rmtree(self.name)
               self.name = None

       @property
       def removed(self):
           return self.name is None

       def __del__(self):
           self.remove()

从 Python 3.4 开始，"__del__()" 方法不会再阻止循环引用被作为垃圾回收，
并且模块全局变量在 *interpreter shutdown* 期间不会被强制设为 "None"。
因此这段代码在 CPython 上应该会正常运行而不会出现任何问题。

然而，"__del__()" 方法的处理会严重地受到具体实现的影响，因为它依赖于解
释器垃圾回收实现方式的内部细节。

更健壮的替代方式可以是定义一个终结器，只引用它所需要的特定函数和对象，
而不是获取对整个对象状态的访问权:

   class TempDir:
       def __init__(self):
           self.name = tempfile.mkdtemp()
           self._finalizer = weakref.finalize(self, shutil.rmtree, self.name)

       def remove(self):
           self._finalizer()

       @property
       def removed(self):
           return not self._finalizer.alive

像这样定义后，我们的终结器将只接受一个对其完成正确清理目录任务所需细节
的引用。 如果对象一直未被作为垃圾回收，终结器仍会在退出时被调用。

基于弱引用的终结器还具有另一项优势，就是它们可被用来为定义由第三方控制
的类注册终结器，例如当一个模块被卸载时运行特定代码:

   import weakref, sys
   def unloading_module():
       # implicit reference to the module globals from the function body
   weakref.finalize(sys.modules[__name__], unloading_module)

注解:

  如果当程序退出时你恰好在守护线程中创建终结器对象，则有可能该终结器不
  会在退出时被调用。 但是，在一个守护线程中 "atexit.register()", "try:
  ... finally: ..." 和 "with: ..." 同样不能保证执行清理。
