weakref ---弱引用

源代码: Lib/weakref.py


这个 weakref 模块允许Python程序员创建 weak references 对象。

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

对对象的弱引用不足以使对象保持活动状态:当对引用的唯一剩余引用是弱引用时, garbage collection 可以随意销毁引用,并将其内存用于其他用途。但是,在实际销毁对象之前,弱引用可能会返回对象,即使没有对它的强引用。

弱引用的主要用途是实现保存大型对象的缓存或映射,在这种情况下,希望大型对象不会仅因为出现在缓存或映射中而保持活动状态。

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

WeakKeyDictionaryWeakValueDictionary 在实现中使用弱引用,在弱引用上设置回调函数,当密钥或值被垃圾回收时,这些弱引用会通知弱字典。 WeakSet 实现 set 接口,但保留对其元素的弱引用,就像 WeakKeyDictionary 做。

finalize 提供一种直接注册清理函数的方法,以便在垃圾收集对象时调用该函数。这比在原始弱引用上设置回调函数更简单,因为模块自动确保终结器在收集对象之前保持活动状态。

大多数程序都应该发现使用这些弱容器类型之一或 finalize 它们只需要——通常不需要直接创建自己的弱引用。低级机械暴露在 weakref 模块有利于高级使用。

并非所有对象都可以弱引用;这些对象可以包括类实例、用python编写的函数(但不是用c编写的)、实例方法、集合、冻结集合、一些 file objectsgenerators ,类型对象、套接字、数组、Deques、正则表达式模式对象和代码对象。

在 3.2 版更改: 添加了对thread.lock、threading.lock和code对象的支持。

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

class Dict(dict):
    pass

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

CPython implementation detail: 其他内置类型,如 tupleint 即使在子类中也不支持弱引用。

扩展类型可以很容易地支持弱引用;请参见 弱参考支撑 .

class weakref.ref(object[, callback])

返回弱引用 object . 如果引用对象仍处于活动状态,则可以通过调用引用对象来检索原始对象;如果引用对象不再处于活动状态,则调用引用对象将导致 None 将被退回。如果 回调 是否提供 None ,并且返回的weakref对象仍处于活动状态,将在对象即将完成时调用回调;弱引用对象将作为唯一参数传递给回调;引用将不再可用。

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

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

弱引用是 hashable 如果 object 是可hash的。即使在 object 已删除。如果 hash() 只有在 object 已被删除,调用将引发 TypeError .

弱引用支持相等的测试,但不支持排序。如果引用仍然存在,则两个引用与其引用具有相同的相等关系(无论 回调 )如果其中一个引用已被删除,则仅当引用对象是同一对象时,引用才相等。

这是子类类型,而不是factory函数。

__callback__

此只读属性返回当前与weakref关联的回调。如果没有回调或weakref的引用不再存在,则此属性将具有值 None .

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

weakref.proxy(object[, callback])

将代理返回到 object 使用弱引用。这支持在大多数上下文中使用代理,而不是要求显式取消对弱引用对象的引用。返回的对象的类型将为 ProxyTypeCallableProxyType ,取决于 object 是可调用的。代理对象不是 hashable 不管引用是什么,这就避免了一些与它们的基本可变性质相关的问题,并防止它们被用作字典键。 回调 与的同名参数相同 ref() 功能。

在 3.8 版更改: 扩展了对代理对象的运算符支持,以包括矩阵乘法运算符 @@= .

weakref.getweakrefcount(object)

返回引用的弱引用和代理的数量 object .

weakref.getweakrefs(object)

返回引用的所有弱引用和代理对象的列表 object .

class weakref.WeakKeyDictionary([dict])

弱引用键的映射类。当不再有对键的强引用时,字典中的条目将被丢弃。这可用于将附加数据与应用程序其他部分拥有的对象相关联,而无需向这些对象添加属性。这对于重写属性访问的对象尤其有用。

在 3.9 版更改: 为添加了支持 ||= 运算符,在中指定 PEP 584 .

WeakKeyDictionary 对象具有直接公开内部引用的附加方法。引用在使用时不能保证是“活动的”,因此在使用之前需要检查调用引用的结果。这可以用来避免创建将导致垃圾收集器将密钥保持在所需时间之外的引用。

WeakKeyDictionary.keyrefs()

返回对键的弱引用的iterable。

class weakref.WeakValueDictionary([dict])

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

在 3.9 版更改: 为添加了支持 ||= 运算符,如 PEP 584 .

WeakValueDictionary 对象具有与 keyrefs() 方法 WeakKeyDictionary 物体。

WeakValueDictionary.valuerefs()

返回对值的弱引用的ITerable。

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 是垃圾收集。与普通的弱引用不同,终结器在收集引用对象之前将一直存在,这大大简化了生命周期管理。

考虑使用终结器 活着的 直到调用它(显式调用或在垃圾收集时调用),然后 dead . 调用实时终结器返回计算结果 func(*arg, **kwargs) ,而调用死终结器返回 None .

在垃圾收集期间,终结器回调引发的异常将显示在标准错误输出上,但无法传播。它们的处理方式与从对象的 __del__() 方法或弱引用的回调。

当程序退出时,将调用其余的活动终结器,除非 atexit 属性已设置为false。它们被称为与创造相反的顺序。

终结器在 interpreter shutdown 当模块全局可能被替换为 None .

__call__()

如果 self 是活动的,然后将其标记为已死并返回调用的结果 func(*args, **kwargs) . 如果 self 死了就回来 None .

detach()

如果 self 是活的,然后标记为死的,然后返回tuple (obj, func, args, kwargs) . 如果 self 死了就回来 None .

peek()

如果 self 是活动的,然后返回元组 (obj, func, args, kwargs) . 如果 self 死了就回来 None .

alive

如果终结器处于活动状态,则为true,否则为false。

atexit

可写布尔属性,默认为true。当程序退出时,它调用所有剩余的活动终结器, atexit 是真的。它们被称为与创造相反的顺序。

注解

必须确保 funcargs关键字参数 不拥有任何引用 obj 直接或间接,否则 obj 永远不会被垃圾收集。特别地, func 不应是的绑定方法 obj .

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 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

例子

这个简单的例子展示了一个应用程序如何使用对象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__() 方法不再阻止引用循环被垃圾收集,并且不再强制模块全局变量 None 在期间 interpreter shutdown . 所以这段代码应该在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

像这样定义的,我们的终结器只接收到一个对需要适当清理目录的详细信息的引用。如果对象从未被垃圾收集,则在退出时仍将调用终结器。

基于weakref的终结器的另一个优点是,它们可以用于为定义由第三方控制的类注册终结器,例如在卸载模块时运行代码:

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: ... 也不要保证清理发生。