Python3标准库:weakref对象的非永久引用
1. weakref对象的非永久引用
weakref模块支持对象的弱引用。正常的引用会增加对象的引用数,并避免它被垃圾回收。但结果并不总是如期望中的那样,比如有时可能会出现一个循环引用,或者有时需要内存时可能要删除对象的缓存。弱引用(weak reference)是一个不能避免对象被自动清理的对象句柄。
1.1 引用
对象的弱引用要通过ref类来管理。要获取原对象,可以调用引用对象。
- import weakref
- class ExpensiveObject:
- def __del__(self):
- print('(Deleting {})'.format(self))
- obj = ExpensiveObject()
- r = weakref.ref(obj)
- print('obj:', obj)
- print('ref:', r)
- print('r():', r())
- print('deleting obj')
- del obj
- print('r():', r())
在这里,由于obj在第二次调用引用之前已经被删除,所以ref返回None。
1.2 引用回调
ref构造函数接受一个可选的回调函数,删除所引用的对象时会调用这个函数。
- import weakref
- class ExpensiveObject:
- def __del__(self):
- print('(Deleting {})'.format(self))
- def callback(reference):
- """Invoked when referenced object is deleted"""
- print('callback({!r})'.format(reference))
- obj = ExpensiveObject()
- r = weakref.ref(obj, callback)
- print('obj:', obj)
- print('ref:', r)
- print('r():', r())
- print('deleting obj')
- del obj
- print('r():', r())
当引用已经“死亡”而且不再引用原对象时,这个回调会接受这个引用对象作为参数。这个特性的一种用法就是从缓存中删除弱引用对象。
1.3 最终化对象
清理弱引用时要对资源完成更健壮的管理,可以使用finalize将回调与对象关联。finalize实例会一直保留(直到所关联的对象被删除) ,即使应用并没有保留最终化对象的引用。
- import weakref
- class ExpensiveObject:
- def __del__(self):
- print('(Deleting {})'.format(self))
- def on_finalize(*args):
- print('on_finalize({!r})'.format(args))
- obj = ExpensiveObject()
- weakref.finalize(obj, on_finalize, 'extra argument')
- del obj
finalize的参数包括要跟踪的对象,对象被垃圾回收时要调用的callable,以及传入这个callable的所有位置或命名参数。
这个finalize实例有一个可写属性atexit,用来控制程序退出时是否调用这个回调(如果还未调用)。
- import sys
- import weakref
- class ExpensiveObject:
- def __del__(self):
- print('(Deleting {})'.format(self))
- def on_finalize(*args):
- print('on_finalize({!r})'.format(args))
- obj = ExpensiveObject()
- f = weakref.finalize(obj, on_finalize, 'extra argument')
- f.atexit = bool(int(sys.argv[1]))
默认设置是调用这个回调。将atexit设置为false会禁用这种行为。
如果向finalize实例提供所跟踪对象的一个引用,这便会导致一个引用被保留,所以这个对象永远不会被垃圾回收。
- import gc
- import weakref
- class ExpensiveObject:
- def __del__(self):
- print('(Deleting {})'.format(self))
- def on_finalize(*args):
- print('on_finalize({!r})'.format(args))
- obj = ExpensiveObject()
- obj_id = id(obj)
- f = weakref.finalize(obj, on_finalize, obj)
- f.atexit = False
- del obj
- for o in gc.get_objects():
- if id(o) == obj_id:
- print('found uncollected object in gc')
如上所示,尽管obj的显式引用已经删除,但是这个对象仍保留,通过f对垃圾回收器可见。
使用所跟踪对象的一个绑定方法作为callable也可以适当地避免对象最终化。
- import gc
- import weakref
- class ExpensiveObject:
- def __del__(self):
- print('(Deleting {})'.format(self))
- def do_finalize(self):
- print('do_finalize')
- obj = ExpensiveObject()
- obj_id = id(obj)
- f = weakref.finalize(obj, obj.do_finalize)
- f.atexit = False
- del obj
- for o in gc.get_objects():
- if id(o) == obj_id:
- print('found uncollected object in gc')
由于为finalize提供的callable是实例obj的一个绑定方法,所以最终化方法保留了obj的一个引用,它不能被删除和被垃圾回收。
1.4 代理
有时使用代理比较弱引用更方便。使用代理可以像使用原对象一样,而且不要求在访问对象之前先调用代理。这说明,可以将代理传递到一个库,而这个库并不知道它接收的是一个引用而不是真正的对象。
- import weakref
- class ExpensiveObject:
- def __init__(self, name):
- self.name = name
- def __del__(self):
- print('(Deleting {})'.format(self))
- obj = ExpensiveObject('My Object')
- r = weakref.ref(obj)
- p = weakref.proxy(obj)
- print('via obj:', obj.name)
- print('via ref:', r().name)
- print('via proxy:', p.name)
- del obj
- print('via proxy:', p.name)
如果引用对象被删除后再访问代理,会产生一个ReferenceError异常。
1.5 缓存对象
ref和proxy类被认为是“底层”的。尽管它们对于维护单个对象的弱引用很有用,并且还支持对循环引用的垃圾回收,但WeakKeyDictionary和WeakValueDictionary类为创建多个对象的缓存提供了一个更适合的API。
WeakValueDictionary类使用它包含的值的弱引用,当其他代码不再真正使用这些值时,则允许垃圾回收。利用垃圾回收器的显式调用,下面展示了使用常规字典和WeakValueDictionary完成内存处理的区别。
- import gc
- from pprint import pprint
- import weakref
- gc.set_debug(gc.DEBUG_UNCOLLECTABLE)
- class ExpensiveObject:
- def __init__(self, name):
- self.name = name
- def __repr__(self):
- return 'ExpensiveObject({})'.format(self.name)
- def __del__(self):
- print(' (Deleting {})'.format(self))
- def demo(cache_factory):
- # hold objects so any weak references
- # are not removed immediately
- all_refs = {}
- # create the cache using the factory
- print('CACHE TYPE:', cache_factory)
- cache = cache_factory()
- for name in ['one', 'two', 'three']:
- o = ExpensiveObject(name)
- cache[name] = o
- all_refs[name] = o
- del o # decref
- print(' all_refs =', end=' ')
- pprint(all_refs)
- print('\n Before, cache contains:', list(cache.keys()))
- for name, value in cache.items():
- print(' {} = {}'.format(name, value))
- del value # decref
- # remove all references to the objects except the cache
- print('\n Cleanup:')
- del all_refs
- gc.collect()
- print('\n After, cache contains:', list(cache.keys()))
- for name, value in cache.items():
- print(' {} = {}'.format(name, value))
- print(' demo returning')
- return
- demo(dict)
- print()
- demo(weakref.WeakValueDictionary)
如果循环变量指示所缓存的值,那么这些循环变量必须被显式清除,以使对象的引用数减少。否则,垃圾回收器不会删除这些对象,它们仍然会保留在缓存中。类似地,all_refs变量用来保存引用,以防止它们被过早地垃圾回收。
WeakKeyDictionary的工作与之类似,不过使用了字典中键的弱引用而不是值的弱引用。
Python3标准库:weakref对象的非永久引用的更多相关文章
- Python3标准库使用样例
原:https://doughellmann.com/blog/the-python-3-standard-library-by-example/the-python-3-standard-libra ...
- 7.Python3标准库--文件系统
''' Python的标准库中包含大量工具,可以处理文件系统中的文件,构造和解析文件名,还可以检查文件内容. 处理文件的第一步是要确定处理的文件的名字.Python将文件名表示为简单的字符串,另外还提 ...
- Python3 标准库
Python3标准库 更详尽:http://blog.csdn.net/jurbo/article/details/52334345 文本 string:通用字符串操作 re:正则表达式操作 diff ...
- 8.Python3标准库--数据持久存储与交换
''' 持久存储数据以便长期使用包括两个方面:在对象的内存中表示和存储格式之间来回转换数据,以及处理转换后数据的存储区. 标准库包含很多模块可以处理不同情况下的这两个方面 有两个模块可以将对象转换为一 ...
- python023 Python3 标准库概览
Python3 标准库概览 操作系统接口 os模块提供了不少与操作系统相关联的函数. >>> import os >>> os.getcwd() # 返回当前的工作 ...
- 比较两个文件的异同Python3 标准库difflib 实现
比较两个文件的异同Python3 标准库difflib 实现 对于要比较两个文件特别是配置文件的差异,这种需求很常见,如果用眼睛看,真是眼睛疼. 可以使用linux命令行工具diff a_file b ...
- python3标准库总结
Python3标准库 操作系统接口 os模块提供了不少与操作系统相关联的函数. ? 1 2 3 4 5 6 >>> import os >>> os.getcwd( ...
- 1.Python3标准库--前戏
Python有一个很大的优势便是在于其拥有丰富的第三方库,可以解决很多很多问题.其实Python的标准库也是非常丰富的,今后我将介绍一下Python的标准库. 这个教程使用的书籍就叫做<Pyth ...
- 记阮一峰---JavaScript 标准参考教程之标准库-Object对象
在看到阮大神的-标准库-Object对象时 有个 类型判断类型 方法可能以后会用到.特此记录一下 4.3:toString()的应用:判断数据类型 Object.prototype.toString方 ...
随机推荐
- JSON解析值富文本
解析前端传递的JSON数据中可能如下 { "result": "<input value="Test" type="button&qu ...
- [bzoj3926] [loj2137] [Zjoi2015] 诸神眷顾的幻想乡
Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝生日. 粉丝们非常热情,自发组织表演了一系列节目给幽香看. ...
- 「 从0到1学习微服务SpringCloud 」09 补充篇-maven父子模块项目
系列文章(更新ing): 「 从0到1学习微服务SpringCloud 」06 统一配置中心Spring Cloud Config 「 从0到1学习微服务SpringCloud 」07 RabbitM ...
- 「 神器 」资源管理神器Clover,风一样的效率
开开心心地上班,这时你得打开我的电脑,点进D盘,打开某个项目;然后还得打开XX文档,还有- 最后的最后,你的桌面便成了这个样子 每天你都得天打开多个文件夹,切换时找文件找的晕头转向而烦恼. 每天层层深 ...
- Java中SMB的相关应用
目录 SMB 服务操作 Ⅰ SMB简介 Ⅱ SMB配置 2.1 Windows SMB Ⅲ 添加SMB依赖 Ⅳ 路径格式 Ⅴ 操作共享 Ⅵ 登录验证 SMB 服务操作 Ⅰ SMB简介 SMB(全称 ...
- springIOC源码接口分析(四):MessageSource
一 定义方法 MessageSource接口用于支持信息的国际化和包含参数的信息的替换 这个接口定义了三个方法: public interface MessageSource { /** * 解析co ...
- cpu负载高简单排查思路
首先通过uptime查看系统负载,然后使用mpstat结合pidstat来初步判断到底是cpu计算量大还是进程争抢过大或者是io过多,接着使用vmstat分析切换次数,以及切换类型,来进一步判断到底是 ...
- top100tools
Top 100 Tools for Learning 2013 2142 EmailShare Here are the Top 100 Tools for Learning 2013 – the ...
- 最小生成树(二)prim
今天为大家带来最小生成树的第二种实现方式,比起kruskal来说,prim相对要复杂一些,在稠密图的表现中表现较好,最优情况下也是nlogn级别. 描述: 1).输入:一个加权连通图,其中顶点集合为V ...
- HDU_3853_区间dp
http://acm.hdu.edu.cn/showproblem.php?pid=3853 dp[i][j]表示由空白串刷成b的从i到j位所需要的最小次数. 然后在比较a和b的每一位,再次更新dp表 ...