8.11. weakref — 弱引用

2.1 新版功能.

源码: Lib/weakref.py


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

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

A weak reference to an object is not enough to keep the object alive: when the only remaining references to a referent are weak references, garbage collection is free to destroy the referent and reuse its memory for something else. A primary use for weak references is to implement caches or mappings holding large objects, where it’s desired that a large object not be kept alive solely because it appears in a cache or mapping.

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

WeakKeyDictionary and WeakValueDictionary use weak references in their implementation, setting up callback functions on the weak references that notify the weak dictionaries when a key or value has been reclaimed by garbage collection. Most programs should find that using one of these weak dictionary types is all they need – it’s not usually necessary to create your own weak references directly. The low-level machinery used by the weak dictionary implementations is exposed by the weakref module for the benefit of advanced uses.

Not all objects can be weakly referenced; those objects which can include class instances, functions written in Python (but not in C), methods (both bound and unbound), sets, frozensets, file objects, generators, type objects, DBcursor objects from the bsddb module, sockets, arrays, deques, regular expression pattern objects, and code objects.

在 2.4 版更改: Added support for files, sockets, arrays, and patterns.

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

几个内建类型如 listdict 不直接支持弱引用,但可以通过子类化添加支持:

class Dict(dict):
    pass

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

CPython implementation detail: Other built-in types such as tuple and long do not support weak references even when subclassed.

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 是否相同)。 如果删除了任一被引用对象,则仅在两个引用对象为同一对象时两者才相等。

在 2.4 版更改: This is now a subclassable type rather than a factory function; it derives from object.

weakref.proxy(object[, callback])

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

weakref.getweakrefcount(object)

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

weakref.getweakrefs(object)

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

class weakref.WeakKeyDictionary([dict])

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

注解

注意:由于 WeakKeyDictionary 是基于 Python 字典构建的,因而在对进行迭代时不可改变其大小。 对于 WeakKeyDictionary 来说要确保这一点可能很困难,因为程序在迭代期间执行的操作可能导致字典中的项“神奇地”消失(这是垃圾回收机制的一个副作用)。

WeakKeyDictionary objects have the following additional methods. These expose the internal references directly. The references are not guaranteed to be “live” at the time they are used, so the result of calling the references needs to be checked before being used. This can be used to avoid creating references that will cause the garbage collector to keep the keys around longer than needed.

WeakKeyDictionary.iterkeyrefs()

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

2.5 新版功能.

WeakKeyDictionary.keyrefs()

Return a list of weak references to the keys.

2.5 新版功能.

class weakref.WeakValueDictionary([dict])

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

注解

注意:由于 WeakValueDictionary 是基于 Python 字典构建的,因而在进行迭代时不可改变其大小。 对于 WeakValueDictionary 来说要确保这一点可能很困难,因为程序在迭代期间执行的操作可能导致字典中的项“神奇”地消失(这是垃圾回收机制的一个副作用)。

WeakValueDictionary objects have the following additional methods. These methods have the same issues as the iterkeyrefs() and keyrefs() methods of WeakKeyDictionary objects.

WeakValueDictionary.itervaluerefs()

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

2.5 新版功能.

WeakValueDictionary.valuerefs()

Return a list of weak references to the values.

2.5 新版功能.

class weakref.WeakSet([elements])

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

2.7 新版功能.

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.11.1. 弱引用对象

Weak reference objects have no attributes or methods, but do allow the referent to be obtained, if it still exists, by calling it:

>>> 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.iteritems():
            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.11.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]