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的工作與之類似,不過使用了字典中鍵的弱引用而不是值的弱引用。
