Python3标准库:weakref对象的非永久引用
Posted 爱编程的小灰灰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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对象的非永久引用的主要内容,如果未能解决你的问题,请参考以下文章
Python数据结构与算法(11)---对象的非永久引用weakref