"weakref" --- 弱引用
********************

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

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

"weakref" 模块允许 Python 程序员创建对象的 *弱引用* 。

在下文中，术语 *所指对象* 表示弱引用所指向的对象。

对象的弱引用不能保证对象存活：当所指对像的引用只剩弱引用时， *垃圾回收
* 可以销毁所指对象，并将其内存重新用于其它用途。但是，在实际销毁对象之
前，即使没有强引用，弱引用也能返回该对象。

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

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

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

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

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

并非所有对象都可以被弱引用。支持弱引用的对象包括类实例、用 Python（而
非用 C）编写的函数、实例方法、集合、冻结集合、某些 *文件对象*、*生成器
*、类型对象、套接字、数组、双端队列、正则表达式模式对象以及代码对象。

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

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

   class Dict(dict):
       pass

   obj = Dict(red=1, green=2, blue=3)   # 此对象是可弱引用的

**CPython 实现细节：** 其他内置类型，如 "tuple" 和 "int"，不支持弱引用
，即使通过子类化也不支持。

可以轻松地使扩展类型支持弱引用；参见 弱引用支持。

当为某个给定类型定义了 "__slots__" 时，弱引用支持会被禁用，除非将
"'__weakref__'" 字符串也加入到 "__slots__" 声明的字符串序列中。 请参阅
__slots__ 文档 了解详情。

class weakref.ref(object[, callback])

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

   允许为同一个对象的构造多个弱引用。每个弱引用注册的回调函数将按从最
   近注册的回调函数，到最早注册的回调函数的顺序调用。

   由回调函数引发的异常将记录于标准错误输入，但无法传播该异常；这些异
   常的处理方式与对象 "__del__()" 方法引发异常的处理方式相同。

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

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

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

   __callback__

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

   在 3.4 版本发生变更: 添加了 "__callback__" 属性。

weakref.proxy(object[, callback])

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

   在所指对象被作为垃圾回收后访问代理对象的属性将引发 "ReferenceError"
   。

   在 3.8 版本发生变更: 扩展代理对象所支持的运算符，包括矩阵乘法运算符
   "@" 和 "@="。

weakref.getweakrefcount(object)

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

weakref.getweakrefs(object)

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

class weakref.WeakKeyDictionary([dict])

   弱引用键的映射类。当不再存在对键的强引用时，字典中的相关条目将被丢
   弃。这可用于将额外数据与应用程序中其它部分拥有的对象相关联，而无需
   向这些对象添加属性。这对于重写了属性访问的对象来说特别有用。

   请注意，当把一个与现有键具有相同值（但是标识号不相等）的键插入字典
   时，它会替换该值，但不会替换现有的键。由于这一点，当删除对原来的键
   的引用时，也将同时删除字典中的对应条目:

      >>> class T(str): pass
      ...
      >>> k1, k2 = T(), T()
      >>> d = weakref.WeakKeyDictionary()
      >>> d[k1] = 1   # d = {k1: 1}
      >>> d[k2] = 2   # d = {k1: 2}
      >>> del k1      # d = {}

   一种变通做法是在重新赋值之前先移除键:

      >>> class T(str): pass
      ...
      >>> k1, k2 = T(), T()
      >>> d = weakref.WeakKeyDictionary()
      >>> d[k1] = 1   # d = {k1: 1}
      >>> del d[k1]
      >>> d[k2] = 2   # d = {k2: 2}
      >>> del k1      # d = {k2: 2}

   在 3.9 版本发生变更: 增加了对 "|" 和 "|=" 运算符的支持，相关说明见
   **PEP 584**。

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

WeakKeyDictionary.keyrefs()

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

class weakref.WeakValueDictionary([dict])

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

   在 3.9 版本发生变更: 增加了对 "|" 和 "|=" 运算符的支持，相关说明见
   **PEP 584**。

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

WeakValueDictionary.valuerefs()

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

class weakref.WeakSet([elements])

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

class weakref.WeakMethod(method[, callback])

   一个模拟对绑定方法（即在类中定义并在实例中查找的方法）进行弱引用的
   自定义 "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()
      >>>

   *callback* 与 "ref()" 函数的同名形参含义相同。

   Added in version 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* 的一个绑定方法。

   Added in version 3.4.

weakref.ReferenceType

   弱引用对象的类型对象。

weakref.ProxyType

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

weakref.CallableProxyType

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

weakref.ProxyTypes

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

参见:

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


弱引用对象
==========

弱引用对象没有 "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 是一个弱引用对象
   o = r()
   if o is None:
       # 引用已被作为垃圾回收
       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().__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().__call__()
           if ob is not None:
               self.__counter += 1
               ob = (ob, self.__counter)
           return ob


示例
====

这个简单的例子演示了一个应用如何使用对象 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]


终结器对象
==========

使用 "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()                                           
(<...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


比较终结器与 "__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():
       # 从函数体隐式地引用模块的 globals
   weakref.finalize(sys.modules[__name__], unloading_module)

备注:

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