8.11. weakref — 弱引用 — Python 文档
8.11. 弱引用 — 弱引用
2.1 版中的新功能。
weakref 模块允许 Python 程序员创建对对象的 弱引用 。
在下文中,术语 referent 表示被弱引用引用的对象。
对对象的弱引用不足以使对象保持活动状态:当对所指对象的唯一剩余引用是弱引用时,垃圾收集 可以自由地销毁所指对象并将其内存重用于其他用途。 弱引用的主要用途是实现保存大对象的缓存或映射,在这种情况下,希望大对象不会仅仅因为它出现在缓存或映射中而保持活动状态。
例如,如果您有许多大型二进制图像对象,您可能希望为每个对象关联一个名称。 如果您使用 Python 字典将名称映射到图像,或将图像映射到名称,则图像对象将保持活动状态,因为它们在字典中作为值或键出现。 由 weakref 模块提供的 WeakKeyDictionary 和 WeakValueDictionary 类是另一种选择,使用弱引用来构造映射,这些映射不会仅仅因为对象出现在映射对象。 例如,如果图像对象是 WeakValueDictionary 中的一个值,那么当对该图像对象的最后剩余引用是弱映射持有的弱引用时,垃圾收集可以回收该对象及其对应的弱映射中的条目被简单地删除。
WeakKeyDictionary 和 WeakValueDictionary 在它们的实现中使用弱引用,在弱引用上设置回调函数,当一个键或值被垃圾收集回收时通知弱字典。 大多数程序应该会发现使用这些弱字典类型之一就是他们所需要的——通常不需要直接创建自己的弱引用。 weakref 模块公开了弱字典实现所使用的低级机制,以便于高级用途。
并非所有对象都可以弱引用; 这些对象可以包括类实例、用 Python 编写的函数(但不是用 C 编写的)、方法(绑定和未绑定)、集合、冻结集、文件对象、generators、类型对象、[ X240X] 来自 bsddb 模块的对象、套接字、数组、双端队列、正则表达式模式对象和代码对象。
2.4 版更改: 添加了对文件、套接字、数组和模式的支持。
2.7 版更改: 添加了对 thread.lock、threading.Lock 和 code 对象的支持。
一些内置类型,例如 list
和 dict 不直接支持弱引用,但可以通过子类化添加支持:
class Dict(dict):
pass
obj = Dict(red=1, green=2, blue=3) # this object is weak referenceable
可以很容易地使扩展类型支持弱引用; 请参阅 弱参考支持 。
- class weakref.ref(object[, callback])
返回对 object 的弱引用。 如果引用对象还活着,则可以通过调用引用对象来检索原始对象; 如果引用对象不再存在,调用引用对象将导致返回 None。 如果提供了 callback 而不是 None,并且返回的 weakref 对象还活着,则在对象即将完成时会调用回调; 弱引用对象将作为唯一参数传递给回调; 参照物将不再可用。
允许为同一个对象构造多个弱引用。 为每个弱引用注册的回调将从最近注册的回调调用到最早注册的回调。
回调引发的异常将在标准错误输出中注明,但不能传播; 它们的处理方式与从对象的
__del__()
方法引发的异常完全相同。如果 对象 是可散列的,则弱引用是 可散列 。 即使在 对象 被删除后,它们也会保持其哈希值。 如果 hash() 仅在删除 object 后第一次调用,则该调用将引发
TypeError
。弱引用支持相等性测试,但不支持排序。 如果引用对象还活着,则两个引用与它们的引用对象具有相同的相等关系(不管 callback)。 如果已删除任一引用对象,则仅当引用对象是同一对象时,引用才相等。
2.4 版更改: 现在是可子类类型而不是工厂函数; 它派生自 object。
- weakref.proxy(object[, callback])
- 返回一个使用弱引用的 object 的代理。 这支持在大多数上下文中使用代理,而不需要与弱引用对象一起使用的显式取消引用。 返回的对象将具有
ProxyType
或CallableProxyType
的类型,具体取决于 object 是否可调用。 无论所指对象如何,代理对象都不是 可散列的 ; 这避免了许多与其基本可变性质相关的问题,并防止将它们用作字典键。 callback与ref()函数的同名参数相同。
- weakref.getweakrefcount(object)
- 返回引用 object 的弱引用和代理的数量。
- weakref.getweakrefs(object)
- 返回引用 object 的所有弱引用和代理对象的列表。
- class weakref.WeakKeyDictionary([dict])
弱引用键的映射类。 当不再有对键的强引用时,字典中的条目将被丢弃。 这可用于将附加数据与应用程序其他部分拥有的对象相关联,而无需向这些对象添加属性。 这对于覆盖属性访问的对象特别有用。
笔记
注意:因为 WeakKeyDictionary 建立在 Python 字典之上,所以在迭代它时不能改变大小。 对于 WeakKeyDictionary,这可能很难确保,因为程序在迭代期间执行的操作可能会导致字典中的项目“神奇地”消失(作为垃圾收集的副作用)。
WeakKeyDictionary 对象具有以下附加方法。 这些直接公开内部引用。 引用不保证在使用时是“活动的”,因此在使用之前需要检查调用引用的结果。 这可用于避免创建引用,这将导致垃圾收集器将键保留的时间超过所需时间。
- WeakKeyDictionary.iterkeyrefs()
返回对键的弱引用的迭代。
2.5 版中的新功能。
- WeakKeyDictionary.keyrefs()
返回键的弱引用列表。
2.5 版中的新功能。
- class weakref.WeakValueDictionary([dict])
弱引用值的映射类。 当不再存在对该值的强引用时,字典中的条目将被丢弃。
笔记
注意:因为 WeakValueDictionary 建立在 Python 字典之上,所以在迭代它时不能改变大小。 对于 WeakValueDictionary,这可能很难确保,因为程序在迭代期间执行的操作可能会导致字典中的项目“神奇地”消失(作为垃圾收集的副作用)。
WeakValueDictionary 对象具有以下附加方法。 这些方法与 WeakKeyDictionary 对象的 iterkeyrefs()
和 keyrefs()
方法具有相同的问题。
- WeakValueDictionary.itervaluerefs()
返回对值的弱引用的迭代。
2.5 版中的新功能。
- WeakValueDictionary.valuerefs()
返回值的弱引用列表。
2.5 版中的新功能。
- class weakref.WeakSet([elements])
设置保持对其元素的弱引用的类。 当不再存在对其的强引用时,元素将被丢弃。
2.7 版中的新功能。
- weakref.ReferenceType
- 弱引用对象的类型对象。
- weakref.ProxyType
- 不可调用的对象代理的类型对象。
- weakref.CallableProxyType
- 可调用对象代理的类型对象。
- weakref.ProxyTypes
- 包含代理的所有类型对象的序列。 这可以更简单地测试对象是否是代理,而无需依赖于对两种代理类型的命名。
- exception weakref.ReferenceError
- 使用代理对象但已收集基础对象时引发异常。 这与标准的 ReferenceError 异常相同。
8.11.1. 弱引用对象
弱引用对象没有属性或方法,但允许通过调用获取引用对象(如果它仍然存在):
>>> 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]