Python 引用的使用量特别多,但引用使用不慎很可能影响垃圾对象回收,这时就需要弱引用解决类似问题。
classDict(dict):
pass
obj = Dict(red=1, green=2, blue=3) # this object is weak referenceable
weakref.ref(object[,callback])
# callback 可选的回调函数,在引用对象被删除时调用
# 此只读属性返回当前关联到弱引用的回调。如果没有回调或者弱引用的引用不再存在,则此属性的值为 None。
__del__()
方法引发的异常完全相同。data = np.array(3)
ref = weak.ref(data)
# ref() += 1 # 这种写法不可以,因为函数无法赋值,弱引用仅能引用
import sys
import numpy as np
import weakref
if __name__ == '__main__':
data = np.random.random([3,3])
# 刚刚建立对象时的引用数
print(sys.getrefcount(data)) # 此时引用数为 2
# 建立弱引用
ref = weakref.ref(data)
# 查看增加弱引用后的引用数
print(sys.getrefcount(data)) # 此时引用数仍为 2,表明弱引用不增加引用数
# 如果为弱引用对象增加强引用,引用数会增加
prox_ref = ref()
print(sys.getrefcount(data)) # 引用数为 3,不要为弱引用对象增加强引用
# 二者输出 id 相同,表明弱引用对象指向同一内存空间
print(id(ref())) # 2809935694304
print(id(data)) # 2809935694304
# 返回为 True 表明二者为同一对象
print(ref() is data) # True
# 对象本身为弱引用对象
print(ref) # <weakref at 0x0000028E3D380A40; to 'numpy.ndarray' at 0x0000028E3D3809E0>
# 类型为 弱引用
print(type(ref)) # <class 'weakref'>
# 引用对象时和原始内容一致
print(type(ref())) # <class 'numpy.ndarray'>
# 数据内容完全一样
print(ref())
print(data)
pass
weakref.proxy(object[, callback])
ProxyType
或 CallableProxyType
类型,具体取决于对象是否可调用。不管引用的对象是什么,代理对象都是不可哈希的; 这样就避免了许多与它们基本的可变性有关的问题,并且防止它们被用作字典键。Callback 与 ref ()函数的同名参数相同。
import sys
import numpy as np
import weakref
if __name__ == '__main__':
data = np.array([1])
# 创建对象,引用数初始为 2
print(sys.getrefcount(data)) # 2
# 创建弱引用
ref = weakref.ref(data)
# 创建弱代理
pro = weakref.proxy(data)
# 此时引用数不变
print(sys.getrefcount(data)) # 2
# 弱引用与原始数据指向同一内存空间
print(id(ref()) == id(data)) # True
# 弱代理则指向不同的对象
print(id(pro) == id(data)) # False
# 代理类型为 <class 'weakproxy'>
print(type(pro)) # <class 'weakproxy'>
# 数据内容和原始数据一致
print(pro) # [1]
print(ref()) # [1]
print(data) # [1]
# 原始数据改动后,代理和引用也会随之更改
data += 1
print(pro) # [2]
print(ref()) # [2]
print(data) # [2]
# 代理数据改动并赋值后,会变为原始数据类型,也就是转换为强引用,此时引用数会增加
pro += 1
print(sys.getrefcount(data)) # 3
# 对象类型变为 <class 'numpy.ndarray'>
print(type(pro)) # <class 'numpy.ndarray'>
print(pro) # [3]
print(ref()) # [3]
# 变为强引用后二者便是同一个对象了
print(pro is data) # True
pass
weakref.getweakrefcount(object)
返回引用对象的弱引用和代理的数量。
weakref.getweakrefs(object)
返回引用对象的所有弱引用和代理对象的列表。
import sys
import numpy as np
import weakref
if __name__ == '__main__':
data = np.array([1])
print(weakref.getweakrefcount(data)) # 初始弱引用数量为 0
# 创建弱引用
ref1 = weakref.ref(data)
print(weakref.getweakrefcount(data)) # 弱引用数量为 1
# 该弱引用对象的强引用数量初始为 2
print(sys.getrefcount(ref1)) # 2
# 创建同一个对象的 第二个 弱引用
ref2 = weakref.ref(data)
# 此时弱引用数量仍为 1
print(weakref.getweakrefcount(data)) # 1
# ref1 弱引用的强引用数量增加了 1
print(sys.getrefcount(ref1)) # 3
# 创建同一个对象的 第三个 弱引用
ref3 = weakref.ref(data)
# 此时弱引用数量仍为 1
print(weakref.getweakrefcount(data)) # 1
# 三个弱引用对象互为强引用,因此强引用数量均为 4
print(sys.getrefcount(ref1)) # 4
print(sys.getrefcount(ref2)) # 4
print(sys.getrefcount(ref3)) # 4
# 对象本身是同一个
print(ref1 is ref2) # True
print(ref2 is ref3) # True
# 创建弱代理
pro1 = weakref.proxy(data)
# 对象弱引用数变为 2
print(weakref.getweakrefcount(data)) # 2
# 创建第二个 若代理 对象
pro2 = weakref.proxy(data)
# 弱引用数量仍为 2
print(weakref.getweakrefcount(data)) # 2
# 弱引用为同一个对象
print(pro1 is pro2) # True
# 弱引用列表
print(weakref.getweakrefs(data))
-->
[<weakref at 0x000001CC700519A0; to 'numpy.ndarray' at 0x000001CC70051940>, <weakproxy at 0x000001CC70EDB630 to numpy.ndarray at 0x000001CC70051940>]
weakref.WeakKeyDictionary([dict])
弱引用键的映射类。当不再有对键的强引用时,字典中的条目将被丢弃。这可用于将附加数据与应用程序其他部分所拥有的对象相关联,而无需向这些对象添加属性。这对于覆盖属性访问的对象特别有用。
WeakKeyDictionary 对象有一个直接公开内部引用的附加方法。引用不能保证在使用时是“活的”,所以调用引用的结果需要在使用前检查。这可以用来避免创建引用,这些引用会导致垃圾收集器将密钥保留得比需要的时间更长。
返回弱引用键值的迭代对象。
weakref.WeakValueDictionary([dict])
弱引用值的映射类。当不再存在对该值的强引用时,字典中的条目将被丢弃。
WeakValueDictionary
对象具有与 WeakKeyDictionary
中 keyrefs()
相同的方法。返回对值的弱引用的迭代。
weakref.WeakSet([elements])
设置保持对其元素的弱引用的类。当不再存在对它的强引用时,将丢弃一个元素。
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!
weakref.finalize(obj, func, /, *args, **kwargs)
func(*arg, **kwargs)
的结果,而调用死终结器返回 None。
__call__()
如果 self 还活着,则将其标记为已死并返回调用 func(*args, **kwargs)
的结果。如果 self 已死,则返回 None。
detach()
如果 self 还活着,则将其标记为已死并返回元组 (obj, func, args, kwargs)
。如果 self 已死,则返回 None。
peek()
如果 self 还活着,则返回元组 (obj, func, args, kwargs)
。如果 self 已死,则返回 None。
如果终结器处于活动状态,则该属性为 true,否则为 false。
一个可写的布尔属性,默认为真。当程序退出时,它会调用 atexit 为 true 的所有剩余实时终结器。它们按创建的相反顺序调用。
weakref.ReferenceType
获取弱引用对象的类型对象。
weakref.ProxyType
返回代理(非方法)数据的类型
weakref.CallableProxyType
返回代理(方法)数据的类型
weakref.ProxyTypes
包含代理的所有类型对象的序列。这可以更简单地测试一个对象是否是一个代理,而不依赖于命名这两种代理类型。
对同一对象的所有弱引用,被组织成一个双向链表,链表头保存在对象中。由于能够创建弱引用的对象类型是多种多样的,很难由一个固定的结构体来表示。因此,Python 在类型对象中提供一个字段 tp_weaklistoffset ,记录弱引用链表头指针在实例对象中的偏移量。
Python 调用一个对象时,执行的是其类型对象中的 tp_call 函数。因此,调用弱引用类型对象 weakref 时,执行的是 weakref 的类型对象,也就是 type 的 tp_call 函数。tp_call 函数则回过头来调用 weakref 的 tp_new 和 tp_init 函数,其中 tp_new 为实例对象分配内存,而 tp_init 则负责初始化实例对象。