8.11. weakref-Weak References

2.1 版中的新Function。

源代码: Lib/weakref.py


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

在下文中,术语“指代”是指被弱引用指代的对象。

对对象的弱引用不足以使该对象保持活动状态:当仅剩余的对引用的引用是弱引用时,garbage collection可以自由销毁该引用并将其内存重用于其他用途。弱引用的主要用途是实现包含大型对象的缓存或 Map,在这种情况下,不希望仅由于大型对象出现在缓存或 Map 中而使其保持活动状态。

例如,如果您有许多大的二进制图像对象,则可能希望为每个对象关联一个名称。如果使用 Python 字典将名称 Map 到图像,或将图像 Map 到名称,则图像对象将保持活动状态,因为它们在字典中以值或键的形式出现。 weakref模块提供的WeakKeyDictionaryWeakValueDictionary类是一种替代方法,它使用弱引用来构造 Map,这些 Map 不会仅仅因为它们出现在 Map 对象中而使对象保持活动状态。例如,如果某个图像对象是WeakValueDictionary中的值,则当对该图像对象的最后剩余引用是弱 Map 所持有的弱引用时,垃圾回收可以回收该对象,并且其在弱 Map 中的对应条目也很简单。已删除。

WeakKeyDictionaryWeakValueDictionary在其实现中使用了弱引用,在弱引用上设置了回调函数,当垃圾回收回收了键或值时,它们会通知弱字典。大多数程序应该发现使用这些弱字典类型之一就足够了–通常不必直接创建自己的弱引用。 weakref模块公开了弱字典实现所使用的低级机制,以利于高级使用。

并非所有对象都可以弱引用。那些对象可以包括类实例,用 Python(但不是用 C 语言编写)的函数,方法(绑定和未绑定),集合,frozensets,文件对象,generator s,类型对象,来自bsddb模块的DBcursor对象,套接字,数组,deque,正则表达式模式对象和代码对象。

在版本 2.4 中进行了更改:添加了对文件,套接字,数组和模式的支持。

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

几种内置类型(例如listdict)不直接支持弱引用,但可以pass子类添加支持:

class Dict(dict):
    pass

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

CPython 实现细节: 诸如tuplelong之类的其他内置类型即使在子类化时也不支持弱引用。

扩展类型可以很容易地支持弱引用。参见参考支持薄弱

    • class * weakref. ref(* object * [,* callback *])
    • 返回对* object 的弱引用。如果引用对象还活着,则可以pass调用引用对象来检索原始对象。如果引用对象不再存在,则调用引用对象将导致返回None。如果提供了 callback *而不是None,并且返回的 weakref 对象仍然有效,则在该对象即将完成时将调用该回调;否则,该回调将被调用。弱引用对象将作为唯一参数传递给回调;该参考对象将不再可用。

可以为同Pair象构造许多弱引用。为每个弱引用注册的回调将从最新注册的回调调用到最早的注册回调。

回调引发的异常将在标准错误输出中记录,但不能传播;它们的处理方式与对象的del()方法引发的异常完全相同。

如果* object 可哈希,则弱引用为hashable。即使删除了 object ,它们也将保持其哈希值。如果仅在删除 object *之后才第一次调用hash(),则该调用将引发TypeError

弱引用支持相等性测试,但不支持 Sequences 测试。如果引用对象仍然存在,则两个引用与其引用对象具有相同的相等关系(无论* callback *如何)。如果已删除任何一个引用对象,则仅当引用对象是同Pair象时,引用才相等。

在版本 2.4 中进行了更改:现在,这是一个可子类化的类型,而不是工厂函数。它源自object

  • weakref. proxy(* object * [,* callback *])

    • 返回使用弱引用的* object 的代理。这支持在大多数情况下使用代理,而不是要求对弱引用对象使用显式取消引用。返回的对象将具有ProxyTypeCallableProxyType的类型,具体取决于 object *是否可调用。代理对象不是hashable,而不管引用对象是什么。这就避免了许多与它们的根本可变性质有关的问题,并避免了将它们用作字典键。 * callback *与ref()函数具有相同名称的参数相同。
  • weakref. getweakrefcount(* object *)

    • 返回引用* object *的弱引用和代理的数量。
  • weakref. getweakrefs(* object *)

    • 返回所有引用* object *的弱引用和代理对象的列表。
    • class * weakref. WeakKeyDictionary([* dict *])
    • 弱引用键的 Map 类。当不再有对该键的强引用时,字典中的条目将被丢弃。这可用于将其他数据与应用程序其他部分拥有的对象相关联,而无需向这些对象添加属性。这对于覆盖属性访问的对象特别有用。

Note

注意:因为WeakKeyDictionary构建在 Python 字典的顶部,所以在对其进行迭代时不得更改大小。对于WeakKeyDictionary来说,这可能很难确保,因为程序在迭代过程中执行的操作可能会导致字典中的项目“因魔术而消失”(作为垃圾回收的副作用)。

WeakKeyDictionary对象具有以下其他方法。这些直接公开内部引用。使用这些引用时,不能保证这些引用是“活动的”,因此在使用这些引用之前,需要检查它们的结果。这可以用来避免创建引用,这些引用将导致垃圾回收器将密钥保留的时间超过所需的时间。

  • WeakKeyDictionary. iterkeyrefs ( )
    • 返回对键的弱引用的迭代。

2.5 版的新Function。

  • WeakKeyDictionary. keyrefs ( )
    • 返回对键的弱引用列表。

2.5 版的新Function。

    • class * weakref. WeakValueDictionary([* dict *])
    • 弱引用值的 Map 类。当不再存在对该值的强引用时,字典中的条目将被丢弃。

Note

注意:因为WeakValueDictionary构建在 Python 字典的顶部,所以在对其进行迭代时不得更改大小。对于WeakValueDictionary来说,这可能很难确保,因为程序在迭代过程中执行的操作可能会导致字典中的项目“因魔术而消失”(作为垃圾回收的副作用)。

WeakValueDictionary对象具有以下其他方法。这些方法与WeakKeyDictionary个对象的iterkeyrefs()keyrefs()方法具有相同的问题。

  • WeakValueDictionary. itervaluerefs ( )
    • 返回对值的弱引用的迭代。

2.5 版的新Function。

  • WeakValueDictionary. valuerefs ( )
    • 返回对值的弱引用列表。

2.5 版的新Function。

  • 类别 weakref. WeakSet([元素])
    • 设置类,该类保留对其元素的弱引用。当不再存在对元素的强引用时,该元素将被丢弃。

2.7 版的新Function。

  • weakref. ReferenceType

    • 弱引用对象的类型对象。
  • weakref. ProxyType

    • 不可调用对象的代理的类型对象。
  • weakref. CallableProxyType

    • 可调用对象代理的类型对象。
  • weakref. ProxyTypes

    • 包含代理的所有类型对象的序列。这可以简化测试对象是否为代理的过程,而不必依赖于命名两种代理类型。
  • exception weakref. ReferenceError

    • 使用代理对象但已收集基础对象时引发异常。这与标准ReferenceErrorexception 相同。

See also

  • PEP 205-Weak References

  • 此Function的建议和基本原理,包括指向较早实现的链接以及有关其他语言中类似Function的信息。

8.11.1. 弱参考对象

弱引用对象没有属性或方法,但是确实允许pass调用来获得引用对象(如果仍然存在):

>>> 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对象的特殊版本可以pass子类创建。在WeakValueDictionary的实现中使用它来减少 Map 中每个条目的内存开销。这可能对将附加信息与引用相关联最有用,但也可以用于在调用中插入附加处理以检索引用对象。

此示例显示了如何使用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. Example

这个简单的示例显示了应用程序如何使用对象 ID 来检索它以前见过的对象。然后,可以将这些对象的 ID 用于其他数据结构中,而不必强制这些对象保持活动状态,但是如果存在,则仍可以pass 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]