python系列(weakref-数据结构之对象的无常引用)
该weakref模块支持对对象的弱引用。正常引用会增加对象上的引用计数,并防止对其进行垃圾回收。这种结果并不总是令人满意的,特别是当可能存在循环引用时或者在需要内存时应删除对象的缓存时。弱引用是对象的句柄,不会阻止它自动清理。
参考资料
通过ref 类管理对对象的弱引用。要检索原始对象,请调用引用对象。
weakref_ref.py
import weakrefclass 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 objprint('r():', r())
在这种情况下,由于obj在第二次调用引用之前被删除,因此ref返回None。
$ python3 weakref_ref.pyobj: <__main__.expensiveobject object="" at="">ref:
参考回调
该ref构造函数接收时被引用的对象被删除时调用一个可选的回调函数。
weakref_ref_callback.py
import weakrefclass 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 objprint('r():', r())
在引用“死”并且不再引用原始对象之后,回调接收引用对象作为参数。此功能的一个用途是从缓存中删除弱引用对象。
$ python3 weakref_ref_callback.pyobj: <__main__.expensiveobject object="" at="">ref:
完成对象
为了在清除弱引用时更好地管理资源,请使用finalize将回调与对象相关联。甲finalize实例被保留,直到连接对象被删除,即使应用程序不保留到终结的参考。
weakref_finalize.py
import weakrefclass 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是要跟踪的对象,在对象被垃圾回收时调用的可调用对象,以及传递给可调用对象的任何位置或命名参数。
$ python3 weakref_finalize.py(Deleting <__main__.expensiveobject object="" at="">)on_finalize(('extra argument',))
该finalize实例具有可写属性,atexit用于控制是否在程序退出时调用回调,如果尚未调用的话。
weakref_finalize_atexit.py
import sysimport weakrefclass 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会禁用该行为。
$ python3 weakref_finalize_atexit.py 1on_finalize(('extra argument',))(Deleting <__main__.expensiveobject object="" at="">)$ python3 weakref_finalize_atexit.py 0
为finalize实例提供对其跟踪的对象的引用会导致保留引用,因此对象永远不会被垃圾回收。
weakref_finalize_reference.py
import gcimport weakrefclass 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 = Falsedel objfor o in gc.get_objects(): if id(o) == obj_id: print('found uncollected object in gc')
如此示例所示,即使obj 删除了显式引用,该对象也会保留并通过垃圾收集器可见f。
$ python3 weakref_finalize_reference.pyfound uncollected object in gc
使用被跟踪对象的绑定方法作为可调用对象也可以防止对象被正确地完成。
weakref_finalize_reference_method.py
import gcimport weakrefclass 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 = Falsedel objfor o in gc.get_objects(): if id(o) == obj_id: print('found uncollected object in gc')
因为给定的callable finalize是实例的绑定方法obj,所以finalize对象持有一个引用obj,该引用 不能被删除和垃圾收集。
$ python3 weakref_finalize_reference_method.pyfound uncollected object in gc
代理
使用代理有时更方便,而不是弱引用。可以使用代理,就好像它们是原始对象一样,并且在对象可访问之前不需要调用代理。因此,它们可以传递给不知道它正在接收引用而不是真实对象的库。
weakref_proxy.py
import weakrefclass 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 objprint('via proxy:', p.name)
如果在删除引用对象后访问代理, ReferenceError则会引发异常。
$ python3 weakref_proxy.pyvia obj: My Objectvia ref: My Objectvia proxy: My Object(Deleting <__main__.expensiveobject object="" at="">)Traceback (most recent call last): File "weakref_proxy.py", line 30, in
缓存对象
在ref和proxy班被认为是“低的水平。”虽然他们是为保持各个对象的弱引用,并允许周期被垃圾收集有用的,但 WeakKeyDictionary和WeakValueDictionary类创建多个对象的缓存提供更合适的API。
本WeakValueDictionary类使用到其持有的价值观弱引用,允许它们在其他代码没有实际使用的时候垃圾收集。使用对垃圾收集器的显式调用说明了内存处理与常规字典之间的区别和WeakValueDictionary:
weakref_valuedict.py
import gcfrom pprint import pprintimport weakrefgc.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') returndemo(dict)print()demo(weakref.WeakValueDictionary)
必须清除任何引用缓存值的循环变量,以便减少对象的引用计数。否则,垃圾收集器将不会删除对象,它们将保留在缓存中。类似地,该all_refs 变量用于保存引用以防止它们过早地被垃圾收集。
$ python3 weakref_valuedict.pyCACHE TYPE:
该WeakKeyDictionary作品类似,但使用的钥匙,而不是在字典中的值弱引用。
警告
查看原文 >>