weakref —弱引用

源代码: Lib/weakref.py


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

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

对对象的弱引用不足以使该对象保持活动状态:当仅剩余的对引用的引用是弱引用时,garbage collection可以自由销毁该引用并将其内存重用于其他用途。但是,直到没有实际破坏对象为止,弱引用可能会返回该对象,即使没有强引用也是如此。

弱引用的主要用途是实现包含大型对象的缓存或 Map,在这种情况下,不希望仅由于大型对象出现在缓存或 Map 中而使其保持活动状态。

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

WeakKeyDictionaryWeakValueDictionary在其实现中使用了弱引用,在弱引用上设置了回调函数,当垃圾回收回收了键或值时,它们会通知弱字典。 WeakSet实现set接口,但是像WeakKeyDictionary一样保留对其元素的弱引用。

finalize提供了一种简单的方法来注册当对象被垃圾回收时调用的清理函数。这比在原始弱引用上设置回调函数更容易使用,因为该模块会自动确保终结器保持活动状态,直到收集对象为止。

大多数程序应该发现只需要使用这些弱容器类型或finalize中的一种即可-通常不必直接创建自己的弱引用。低级机械由weakref模块公开,以供高级使用。

并非所有对象都可以弱引用。这些对象可以包括类实例,用 Python(但不是用 C 语言编写)的函数,实例方法,集合,frozenset,某些file objectsgenerators,类型对象,套接字,数组,deque,正则表达式模式对象和代码对象。

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

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

class Dict(dict):
    pass

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

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

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

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

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

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

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

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

这是可分类的类型,而不是工厂函数。

  • __callback__
    • 此只读属性返回当前与 weakref 相关联的回调。如果没有回调,或者弱引用的引用不再存在,则此属性的值为None

在版本 3.4 中进行了更改:添加了callback属性。

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

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

  • weakref. getweakrefcount(* object *)

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

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

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

  • WeakKeyDictionary. keyrefs ( )

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

WeakValueDictionary对象具有一个附加方法,该方法与WeakKeyDictionary对象的keyrefs()方法具有相同的问题。

  • WeakValueDictionary. valuerefs ( )

    • 返回对值的弱引用的迭代。
  • 类别 weakref. WeakSet([元素])

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

    • 自定义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 版的新Function。

    • class * weakref. finalize(* obj func *, *args * kwargs *)
    • 返回一个可调用的终结器对象,当* obj *被垃圾回收时将被调用。与普通的弱引用不同,终结器将始终存在直到收集引用对象为止,从而大大简化了生命周期 Management。

终结器在被调用之前(被显式地或在垃圾回收时)被认为是“活着的”,之后被称为“死的”。调用实时终结器将返回评估func(*arg, **kwargs)的结果,而调用无效终结器将返回None

在垃圾回收过程中,终结器回调引发的异常将显示在标准错误输出中,但不能传播。它们的处理方式与从对象的del()方法或弱引用的回调引发的异常相同。

程序退出时,将调用每个剩余的实时终结器,除非其atexit属性设置为 false。它们的创建 Sequences 相反。

当模块全局变量很可能已被None替换时,终结器将永远不会在interpreter shutdown的后期调用其回调。

  • __call__ ( )

    • 如果* self 仍然存在,则将其标记为死亡,并返回调用func(*args, **kwargs)的结果。如果 self *已死,则返回None
  • detach ( )

    • 如果* self 仍然存在,则将其标记为死亡并返回 Tuples(obj, func, args, kwargs)。如果 self *已死,则返回None
  • peek ( )

    • 如果* self 还活着,则返回 Tuples(obj, func, args, kwargs)。如果 self *已死,则返回None
  • alive

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

    • 可写的布尔属性,默认情况下为 true。程序退出时,它将调用atexit为 true 的所有剩余实时终结器。它们的创建 Sequences 相反。

Note

重要的是要确保* func args kwargs 不直接或间接拥有对 obj 的任何引用,因为否则将永远不会垃圾收集 obj 。特别地, func 不应是 obj *的绑定方法。

3.4 版的新Function。

  • weakref. ReferenceType

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

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

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

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

See also

  • PEP 205-Weak References

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

弱参考对象

弱引用对象除了ref.callback之外,没有方法,也没有属性。弱引用对象可以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.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

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]

Finalizer Objects

使用finalize的主要好处在于,它很容易注册回调,而无需保留返回的 finalizer 对象。例如

>>> 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()方法,或者

  • 程序退出。

我们可以try使用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()方法的处理是特定于实现的,因为它取决于解释器的垃圾收集器实现的内部细节。

一个更健壮的替代方法是定义一个终结器,该终结器仅引用其所需的特定Function和对象,而不能访问该对象的完整状态:

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

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

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

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

Note

如果在程序退出时在守护线程中创建终结器对象,则有可能在退出时未调用终结器。但是,在守护线程atexit.register()try: ... finally: ...with: ...中也不保证清除也会发生。