Python之weakref模块的使用
weakref模块的作用
weakref模块支持对象的弱引用。正常的引用会增加对象的引用数,并避免它被垃圾回收。但结果并不是如期望中那样,比如有时可能会出现一个循环引用,
或者有时需要内存时可能要删除对象的缓存。弱引用是一个不能避免对象被自动清理的对象句柄。
1、对象的弱引用
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: <__main__.ExpensiveObject object at 0x000001E72262EB88> ref: <weakref at 0x000001E722633598; to 'ExpensiveObject' at 0x000001E72262EB88> r(): <__main__.ExpensiveObject object at 0x000001E72262EB88> deleting obj (Deleting <__main__.ExpensiveObject object at 0x000001E72262EB88>) r(): None
2、对象引用回调
import weakref class ExpensiveObject: def __del__(self): print('(Deleting {})'.format(self)) def callback(reference): """删除对象后,进行回调""" 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())
运行效果
obj: <__main__.ExpensiveObject object at 0x000002819406EE08> ref: <weakref at 0x0000028194073688; to 'ExpensiveObject' at 0x000002819406EE08> r(): <__main__.ExpensiveObject object at 0x000002819406EE08> deleting obj (Deleting <__main__.ExpensiveObject object at 0x000002819406EE08>) callback(<weakref at 0x0000028194073688; dead>) r(): None
3、清理的弱引用,最终回收处理
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
运行效果
(Deleting <__main__.ExpensiveObject object at 0x00000188B703EB88>) on_finalize(('extra argument',))
4、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]))
运行效果
python3 weakref_finalize_atexit.py 1 on_finalize(('extra argument',)) (Deleting <__main__.ExpensiveObject object at 0x00000295B11FED08>) python3 weakref_finalize_atexit.py 0
5、在finalize引用对象的时候,del对象,内存中的对象是不会被gc回收的
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('在GC中发现未收集的对象')
运行效果
在GC中发现未收集的对象
6、finalize提供callable是实例obj的一个绑定方法,所以最终化方法保留了obj的一个引用,它不能被删除和被回收。
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('在GC中发现未收集的对象')
运行效果
在GC中发现未收集的对象
7、代理弱引用
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)
运行效果
via obj: My Object via ref: My Object via proxy: My Object (Deleting <__main__.ExpensiveObject object at 0x000001FF9D3FECC8>) Traceback (most recent call last): File "xxxx", line 21, in <module> print('via proxy:', p.name) ReferenceError: weakly-referenced object no longer exists #代理对象不存在的时候,报异常
8、弱引用缓存对象
from pprint import pprint import weakref import gc 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): # 保持对象,以便任何弱引用,不会立即移除 all_refs = {} # 使用工厂创建缓存 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 # 删除对对象的所有引用,但缓存除外 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)
运行效果
CACHE TYPE: <class 'dict'> all_refs = {'one': ExpensiveObject(one), 'three': ExpensiveObject(three), 'two': ExpensiveObject(two)} Before, cache contains: ['one', 'two', 'three'] one = ExpensiveObject(one) two = ExpensiveObject(two) three = ExpensiveObject(three) Cleanup: After, cache contains: ['one', 'two', 'three'] one = ExpensiveObject(one) two = ExpensiveObject(two) three = ExpensiveObject(three) demo returning (Deleting ExpensiveObject(one)) (Deleting ExpensiveObject(two)) (Deleting ExpensiveObject(three)) CACHE TYPE: <class 'weakref.WeakValueDictionary'> all_refs = {'one': ExpensiveObject(one), 'three': ExpensiveObject(three), 'two': ExpensiveObject(two)} Before, cache contains: ['one', 'two', 'three'] one = ExpensiveObject(one) two = ExpensiveObject(two) three = ExpensiveObject(three) Cleanup: (Deleting ExpensiveObject(one)) (Deleting ExpensiveObject(two)) (Deleting ExpensiveObject(three)) After, cache contains: [] demo returning