1.概念

弱引用:不会增加对象的引用数量,不会妨碍所指对象(referent)被当作垃圾回收。弱引用可用于解决循环引用的问题。

弱引用在缓存应用中很有用,因为不想仅仅因为对象被缓存引用着而始终被保持。

2.弱引用的创建
2.1创建弱引用

通过调用 weakref 模块的 ref(obj[,callback]) 来创建一个弱引用,obj 是你想弱引用的对象, callback 是一个可选的函数,当因没有引用导致 Python 要销毁这个对象时调用。回调函数callback要求单个参数(弱引用的对象)。

一旦你有了一个对象的弱引用,你就能通过调用弱引用来获取被弱引用的对象。

>>> import sys
>>> import weakref
>>> class Man():
...     def __init__(self, name):
...             self.name = name
...
>>> man0 = Man('zhe')    # 增加一个引用  count = 1 
>>> sys.getrefcount(man0)
2
>>> r = weakref.ref(man0)   # 增加一个弱引用  count = 1  
>>> sys.getrefcount(man0)
2
>>> r   # 获取弱引用所指向的对象
<weakref at 0x0000026AF3974688; to 'Man' at 0x0000026AF398C710>
>>> man1 = r()
>>> man0 is man1
True
>>> sys.getrefcount(man0)
3
>>> man0 = None
>>> man1 = None
>>> r   # 当对象引用计数为零时,弱引用失效。
<weakref at 0x0000026AF3974688; dead>

weakref.ref 类的实例获取所指对象,提供的是底层接口,尽量不要手动创建并处理weakref.ref实例。

注:

  • 上面的代码中,我们使用 sys 包中的 getrefcount() 来查看某个对象的引用计数。需要注意的是,当使用某个引用作为参数,传递给 getrefcount() 时,参数实际上创建了一个临时的引用。因此, getrefcount() 所得到的结果,会比期望的多 1 。
  • 一旦没有了对这个对象的其它的引用,调用弱引用将返回None,因为Python已经销毁了这个对象。 注意:大部分的对象不能通过弱引用来访问。
  • weakref 模块中的 getweakrefcount(obj) 和 getweakrefs(obj) 分别返回弱引用数和关于所给对象的引用列表。
  • 弱引用对于创建对象(这些对象很费资源)的缓存是有用的。
  • 对弱引用的使用来访问原对象要通过 weakref() 的形式。 其实可以创建代理对象对原对象进行访问。
2.2创建代理对象

代理对象 是弱引用对象,它们的行为就像它们所引用的对象,这就便于你不必首先调用弱引用来访问背后的对象。通过weakref模块的proxy(obj[,callback])函数来创建代理对象。使用代理对象就如同使用对象本身一样:

相比于创建弱引用 在调用上完全等同于

>>> import sys
>>> import weakref
>>> class Man():
...     def __init__(self, name):
...             self.name = name
...
>>> def callback_ref(self):
...     print (self)
...     print ("callback_ref")
...
>>> def callback_proxy(self):
...     print (self)
...     print ("callback_proxy")
...
>>> man = Man('zhe')   # 引用计数 +1
>>> sys.getrefcount(man)
2
>>> man_ref = weakref.ref(man, callback_ref)  # 弱引用 引用计数不变
>>> sys.getrefcount(man)
2
>>> man_ref   # 弱引用对象
<weakref at 0x0000019A63664638; to 'Man' at 0x0000019A6367C668>
>>> man_ref.name   # 对原对象的访问形式错误
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'weakref' object has no attribute 'name'
>>> man_ref().name    # 正确的对原对象的访问形式
'zhe'
>>> man_proxy = weakref.proxy(man, callback_proxy)   # 使用代理 引用计数不变
>>> sys.getrefcount(man)
2
>>> man_proxy  # 代理对象
<weakproxy at 0x0000019A634D6BD8 to Man at 0x0000019A6367C668>
>>> man_proxy.name   # 访问形式与原引用相同
'zhe'
>>> del man   # 注
Exception ignored in: <function callback_proxy at 0x0000019A636807B8>
Traceback (most recent call last):
  File "<stdin>", line 2, in callback_proxy
ReferenceError: weakly-referenced object no longer exists
<weakref at 0x0000019A63664638; dead>
callback_ref
2.3缓存对象

一般不要直接创建处理 weakref.ref 实例,最好使用 weakref 集合和 finalize,即 WeakKeyDictionary、WeakValueDictionary、WeakSet、finalize。

2.3.1 WeakValueDictionary

WeakValueDictionary 类实现的是一种可变映射,值是对象的弱引用,被引用的对象被回收后,对应的键会主动删除,常用于缓存。

import weakref


class Cheese:

    def __init__(self, kind):
        self.kind = kind

    def __repr__(self):
        return 'Cheese(%r)' % self.kind


stock = weakref.WeakValueDictionary()
catalog = [Cheese('A'), Cheese('B'), Cheese('C'), Cheese('D')]

for cheese in catalog:
    stock[cheese.kind] = cheese
# for key in stock.keys():
#     print(key)
print(sorted(stock.keys()))
# 删除了 catalog 列表,对应的值弱引用字典中对应的键被删除,但是 'D' 还存在一个引用,for循环中的 cheese,它是全局变量
del catalog
print(sorted(stock.keys()))
# 删除了 cheese 之后,所有键被删除
del cheese
print(sorted(stock.keys()))


"""
运行结果:
['A', 'B', 'C', 'D']
['D']
[]

Process finished with exit code 0
"""
2.3.2 WeakKeyDictionary

WeakKeyDictionary与WeakValueDictionary对应,它存储的键是对象的弱引用。

import weakref


class Cheese:

    def __init__(self, kind):
        self.kind = kind

    def __repr__(self):
        return 'Cheese(%r)' % self.kind


stock = weakref.WeakKeyDictionary()
catalog = [Cheese('A'), Cheese('B'), Cheese('C'), Cheese('D')]

for cheese in catalog:
    stock[cheese] = cheese.kind
# for value in stock.values():
#     print(value)
print(sorted(stock.values()))
# 删除了 catalog 列表,对应的键弱引用字典中对应的值被删除,但是 'D' 还存在一个引用,for循环中的 cheese,它是全局变量
del catalog
print(sorted(stock.values()))
# 删除了 cheese 之后,所有键被删除
del cheese
print(sorted(stock.values()))


"""
运行结果:
['A', 'B', 'C', 'D']
['D']
[]

Process finished with exit code 0
"""
2.3.3 WeakSet
import weakref


class Cheese:

    def __init__(self, kind):
        self.kind = kind

    def __repr__(self):
        return 'Cheese(%r)' % self.kind


a = Cheese("zhangsan")
b = Cheese("lisi")
c = Cheese("wangwu")
d = Cheese("zhaoliu")
weak_set = weakref.WeakSet()
weak_set.add(a)
weak_set.add(b)
weak_set.add(c)
weak_set.add(d)
for item in weak_set:
    print(item)
print(weak_set.pop())



"""
运行结果:
Cheese('wangwu')
Cheese('zhangsan')
Cheese('lisi')
Cheese('zhaoliu')
Cheese('wangwu')

Process finished with exit code 0
"""

操作可参照普通集合的成员方法。

2.3.4 finalize

[参考博客]
https://www.jianshu.com/p/6cc7a98fdb81https://www.baidu.com/link?url=drZfhkNPyI4NpZYAtsdwkn7svlXG6wn3cubwGFUgoYhG58h-nWKXb5N71P-h-Io2pvJjliPvuqxeM15XIsQorkGlHb7GwsfEjhYQsYkA50S&wd=&eqid=ac3662e2001b203f000000035f0d4fe2