Python 引用的使用量特别多,但引用使用不慎很可能影响垃圾对象回收,这时就需要弱引用解决类似问题。
classDict(dict):
pass
obj = Dict(red=1, green=2, blue=3) # this object is weak referenceableweakref.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)
passweakref.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
passweakref.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 则负责初始化实例对象。