Python weakref 弱引用
Python weakref 弱引用
要理解weakref核心在于Python的垃圾回收机制及其包含的引用计数
引用计数
import sys
a = "cnblogs.sgt"
# 使用 sys.getrefcount 查看变量引用计数
# 注意下:函数 sys.getrefcount() 调用时会增加一个引用,调用完又销毁一个引用。 实际上就是传参的问题
# 所以这里a的引用计数不出意外因该是2,如果你使用的是三方IDE,可能会更多,建议使用命令行查看
a_ref_count = sys.getrefcount(a) # 结果为2
# 定义一个变量a,a的值存入内存,它被打上标识a的标签
# 此时存入内存地址的1这个值便有了一个引用,其引用计数+1
b = a
a_ref_count = sys.getrefcount(a) # 结果为3
# 当我将a的值赋值给b这个变量时候,原来1这个值存入内存的标签则多了一个b,此时存入内存地址的a的值便多了一个引用,其引用计数+1
a_list = []
a_list.append(a)
a_ref_count = sys.getrefcount(a) # 结果为4
引用计数增加
- 创建对象,用变量接收 :
name = '123'
- 对象被其他的变量引用 :
name2 = name
- 对象被其他的对象引用(放入其他容器类型的数据中)
li = [11, 22, name]
- 当成函数传入函数中时
def func(n):
print(n)
func(name)
引用计数减少
- 变量被显式销毁
del name
- 引用对象的变量指向了其他对象(变量被重新赋了个别的值)
name2 = ‘abc’
- 从其他对象(容器类型数据中移除时),
li.pop()
- 函数执行完毕之后,内部作用域的变量会被销毁
Python数据池机制
- 小整数池: -5 到 256之间的整数,python做了一个数据池实现创建好所有的数据,当程序中使用这个范围内的对象,不会重新创建,直接引用
- 字符串驻留池
intern
机制:由纯单词字符(数字、字母、下划线)组成的字符串,python也做了优化纯单字符组成的字符串,在创建之后会被加入字符串驻留池中,如果程序中再次定义相同的字符串,则直接引用
重点来了:以上所说的引用都是强引用(相对于弱引用而言)
弱引用
相对于强引用时候增加引用会增加引用计数,弱引用也会增加引用,但是引用计数不变
弱引用不会增加对象的引用数量。引用的目标对象称为 所指对象 (referent)。因此,弱引用不会妨碍所指对象被当作垃圾回收。
弱引用在缓存应用中很有用,因为不想仅因为被缓存引用着而始终保存缓存对象。
使用 weakref.ref
实例可以获取所指对象。如果对象存在,调用弱引用可以获取对象;否则返回 None
。
weakref.ref
类其实是低层接口,供高级用途使用,多数程序最好使用 weakref 工具集 和 finalize
。
weakref 工具集合:
WeakKeyDictionary
:WeakValueDictionary
: 这是一种可变映射,里面的值是对象的弱引用。被引用的对象在程序中的其他地方被当作垃圾回收后,对应的键会自动从WeakValueDictionary
中删除。因此,WeakValueDictionary
经常用于缓存。WeakSet
: 保存元素弱引用的集合类。元素没有强引用时,集合会把它删除。finalize
(内部使用弱引用)
如果一个类需要知道所有实例,一种好的方案是创建一个 WeakSet 类型的类属性,保存实例的引用。
如果使用常规的 set
,实例永远不会被垃圾回收,因为类中有实例的强引用,而类存在的时间与 Python 进程一样长,除非显式删除类。
弱引用的局限性可参考官方文档
# 前提: Python 控制台会自动把 _ 变量绑定到结果不为 None 的表达式结果上。
In [1]: import weakref
In [2]: a_set = {0,1}
In [3]: wref = weakref.ref(a_set) # 弱引用对象 wref
In [4]: wref
Out[4]: <weakref at 0x10f29aea8; to 'set' at 0x10e906f28>
In [5]: wref() # 返回的是被引用的对象,{0, 1}。因为是控制台会话,所以 {0, 1} 会绑定给 _ 变量。
Out[5]: {0, 1}
In [6]: a_set = {2, 3, 4} # a_set 不再指代 {0, 1} 集合,因此集合的引用数量减少了。但是 _ 变量仍然指代它。
In [7]: wref()
Out[7]: {0, 1}
In [8]: wref() is None # 计算这个表达式时,{0, 1} 存在,因此 wref() 不是 None。但是,随后 _ 绑定到结果值 False。现在 {0, 1} 没有强引用了。
Out[8]: False
In [9]: wref() is None # 因为 {0, 1} 对象不存在了,所以 wref() 返回 None。
Out[9]: Ture
WeakValueDictionary 示例:
class Cheese:
def __init__(self, kind):
self.kind = kind
def __repr__(self):
return 'Cheese(%r)' % self.kind
# 执行:
In [2]: import weakref
In [3]: stock = weakref.WeakValueDictionary() # 创建弱引用字典实例。
In [4]: catalog = [Cheese('Read Leicester'), Cheese('Tilsit'),Cheese('Brie'), Cheese('Parmesan')]
In [6]: for cheese in catalog:
...: stock[cheese.kind] = cheese # 名称映射到实例. [弱引用]
...:
In [7]: sorted(stock.keys())
Out[7]: ['Brie', 'Parmesan', 'Read Leicester', 'Tilsit']
In [8]: del catalog
In [9]: sorted(stock.keys()) # 为什么还剩一个? 因为临时变量。
Out[9]: ['Parmesan']
In [10]: del cheese
In [11]: sorted(stock.keys()) # 临时变量删除后,为空.
Out[11]: []
>>> import weakref
>>>
>>> class C: # 这里新建一个类,因为WeakValueDictionary()
... def __init__(self, value): # 要求value是一个obj
... self.value = value
...
>>> def test_weak_value_dict():
... d= weakref.WeakValueDictionary()
... k1 = 'test1'
... v1 = C(1) # 这时候C(1)是有一个强引用的:v1
... d[k1] = v1 # 这个语句也就是字典赋值,但是由于我们用的
... print(d[k1]) # WeakValueDictionary(),所以字典里的是弱引用
... del v1 # 这时候删除了C(1)唯一的强引用 v1,因此
... print(d[k1]) # WeakValueDictionary()里面 k1,v1 这个键值对消失了
...
>>> test_weak_value_dict()
<__main__.C object at 0x000001793E545198>
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 8, in test_weak_value_dict
File "C:\Users\ASUS\Anaconda3\lib\weakref.py", line 137, in __getitem__
o = self.data[key]()
KeyError: 'test1'
>>>
weakref.ref()
和weakref.proxy()
的区别
我们在使用weak.ref
时,返回值ref
,需要执行ref()
才是弱引用的对象,ref()
相当于 c_obj
而weakref.proxy
的返回值直接就是弱引用的对象,返回值proxy
直接相当于c_obj
请相信自己
当我们迷茫,懒惰,退缩的时候 我们会格外的相信命运 相信一切都是命中注定
而当我们努力拼搏,积极向上时 我们会格外的相信自己
所以命运是什么呢? 它是如果你习惯它 那它就会一直左右你
如果你想挣脱它 那它就成为你的阻碍 可如果你打破了它 那它就是你人生的垫脚石!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!