weakref --- 弱引用?
源碼: Lib/weakref.py
weakref 模塊允許Python程序員創(chuàng)建對象的 weak references 。
在下文中,術(shù)語 referent 表示由弱引用引用的對象。
對對象的弱引用不能保證對象存活:當(dāng)對像的引用只剩弱引用時(shí), garbage collection 可以銷毀引用并將其內(nèi)存重用于其他內(nèi)容。但是,在實(shí)際銷毀對象之前,即使沒有強(qiáng)引用,弱引用也一直能返回該對象。
弱引用的主要用途是實(shí)現(xiàn)保存大對象的高速緩存或映射,但又不希望大對象僅僅因?yàn)樗霈F(xiàn)在高速緩存或映射中而保持存活。
例如,如果您有許多大型二進(jìn)制圖像對象,則可能希望將名稱與每個(gè)對象關(guān)聯(lián)起來。如果您使用Python字典將名稱映射到圖像,或?qū)D像映射到名稱,則圖像對象將保持活動狀態(tài),因?yàn)樗鼈冊谧值渲酗@示為值或鍵。 weakref 模塊提供的 WeakKeyDictionary 和 WeakValueDictionary 類可以替代Python字典,使用弱引用來構(gòu)造映射,這些映射不會僅僅因?yàn)樗鼈兂霈F(xiàn)在映射對象中而使對象保持存活。例如,如果一個(gè)圖像對象是 WeakValueDictionary 中的值,那么當(dāng)對該圖像對象的剩余引用是弱映射對象所持有的弱引用時(shí),垃圾回收可以回收該對象并將其在弱映射對象中相應(yīng)的條目刪除。
WeakKeyDictionary 和 WeakValueDictionary 在它們的實(shí)現(xiàn)中使用弱引用,在弱引用上設(shè)置回調(diào)函數(shù),當(dāng)鍵或值被垃圾回收回收時(shí)通知弱字典。 WeakSet 實(shí)現(xiàn)了 set 接口,但像 WeakKeyDictionary 一樣,只持有其元素的弱引用。
finalize 提供了注冊一個(gè)對象被垃圾收集時(shí)要調(diào)用的清理函數(shù)的方式。這比在原始弱引用上設(shè)置回調(diào)函數(shù)更簡單,因?yàn)槟K會自動確保對象被回收前終結(jié)器一直保持存活。
這些弱容器類型之一或者 finalize 就是大多數(shù)程序所需要的 - 通常不需要直接創(chuàng)建自己的弱引用。weakref 模塊暴露了低級機(jī)制,以便于高級用途。
并非所有對象都可以被弱引用;可以被弱引用的對象包括類實(shí)例,用 Python(而不是用 C)編寫的函數(shù),實(shí)例方法、集合、凍結(jié)集合,某些 文件對象,生成器,類型對象,套接字,數(shù)組,雙端隊(duì)列,正則表達(dá)式模式對象以及代碼對象等。
在 3.2 版更改: 添加了對thread.lock,threading.Lock和代碼對象的支持。
幾個(gè)內(nèi)建類型如 list 和 dict 不直接支持弱引用,但可以通過子類化添加支持:
class Dict(dict):
pass
obj = Dict(red=1, green=2, blue=3) # this object is weak referenceable
Extension types can easily be made to support weak references; see Weak Reference Support.
-
class
weakref.ref(object[, callback])? 返回對 對象 的弱引用。如果原始對象仍然存活,則可以通過調(diào)用引用對象來檢索原始對象;如果引用的原始對象不再存在,則調(diào)用引用對象將得到
None。如果提供了 回調(diào) 而且值不是None,并且返回的弱引用對象仍然存活,則在對象即將終結(jié)時(shí)將調(diào)用回調(diào);弱引用對象將作為回調(diào)的唯一參數(shù)傳遞;指示物將不再可用。許多弱引用也允許針對相同對象來構(gòu)建。 為每個(gè)弱引用注冊的回調(diào)將按從最近注冊的回調(diào)到最早注冊的回調(diào)的順序被調(diào)用。
回調(diào)所引發(fā)的異常將記錄于標(biāo)準(zhǔn)錯誤輸出,但無法被傳播;它們會按與對象的
__del__()方法所引發(fā)的異常相同的方式被處理。如果 object 可哈希,則弱引用也為 hashable。 即使在 object 被刪除之后它們?nèi)詫⒈3制涔V怠?如果
hash()在 object 被刪除之后才首次被調(diào)用,則該調(diào)用將引發(fā)TypeError。弱引用支持相等檢測,但不支持排序比較。 如果被引用對象仍然存在,兩個(gè)引用具有與它們的被引用對象一致的相等關(guān)系(無論 callback 是否相同)。 如果刪除了任一被引用對象,則僅在兩個(gè)引用對象為同一對象時(shí)兩者才相等。
這是一個(gè)可子類化的類型而非一個(gè)工廠函數(shù)。
-
__callback__? 這個(gè)只讀屬性會返回當(dāng)前關(guān)聯(lián)到弱引用的回調(diào)。 如果回調(diào)不存在或弱引用的被引用對象已不存在,則此屬性的值為
None。
在 3.4 版更改: 添加了
__callback__屬性。-
-
weakref.proxy(object[, callback])? 返回 object 的一個(gè)使用弱引用的代理。 此函數(shù)支持在大多數(shù)上下文中使用代理,而不要求顯式地對所使用的弱引用對象解除引用。 返回的對象類型將為
ProxyType或CallableProxyType,具體取決于 object 是否可調(diào)用。 Proxy 對象不是 hashable 對象,無論被引用對象是否可哈希;這可避免與它們的基本可變性質(zhì)相關(guān)的多種問題,并可防止它們被用作字典鍵。 callback 與ref()函數(shù)的同名形參含義相同。
-
weakref.getweakrefcount(object)? 返回指向 object 的弱引用和代理的數(shù)量。
-
weakref.getweakrefs(object)? 返回由指向 object 的所有弱引用和代理構(gòu)成的列表。
-
class
weakref.WeakKeyDictionary([dict])? 弱引用鍵的映射類。 當(dāng)不再有對鍵的強(qiáng)引用時(shí)字典中的條目將被丟棄。 這可被用來將額外數(shù)據(jù)關(guān)聯(lián)到一個(gè)應(yīng)用中其他部分所擁有的對象而無需在那些對象中添加屬性。 這對于重載了屬性訪問的對象來說特別有用。
WeakKeyDictionary 對象具有一個(gè)額外方法可以直接公開內(nèi)部引用。 這些引用不保證在它們被使用時(shí)仍然保持“存活”,因此這些引用的調(diào)用結(jié)果需要在使用前進(jìn)行檢測。 此方法可用于避免創(chuàng)建會導(dǎo)致垃圾回收器將保留鍵超出實(shí)際需要時(shí)長的引用。
-
WeakKeyDictionary.keyrefs()? 返回包含對鍵的弱引用的可迭代對象。
-
class
weakref.WeakValueDictionary([dict])? 弱引用值的映射類。 當(dāng)不再有對鍵的強(qiáng)引用時(shí)字典中的條目將被丟棄。
WeakValueDictionary 對象具有一個(gè)額外方法,此方法存在與 WeakKeyDictionary 對象的 keyrefs() 方法相同的問題。
-
WeakValueDictionary.valuerefs()? 返回包含對值的弱引用的可迭代對象。
-
class
weakref.WeakSet([elements])? 保持對其元素弱引用的集合類。 當(dāng)不再有對某個(gè)元素的強(qiáng)引用時(shí)元素將被丟棄。
-
class
weakref.WeakMethod(method)? 一個(gè)模擬對綁定方法(即在類中定義并在實(shí)例中查找的方法)進(jìn)行弱引用的自定義
ref子類。 由于綁定方法是臨時(shí)性的,標(biāo)準(zhǔn)弱引用無法保持它。WeakMethod包含特別代碼用來重新創(chuàng)建綁定方法,直到對象或初始函數(shù)被銷毀:>>> class C: ... def method(self): ... print("method called!") ... >>> c = C() >>> r = weakref.ref(c.method) >>> r() >>> r = weakref.WeakMethod(c.method) >>> r() <bound method C.method of <__main__.C object at 0x7fc859830220>> >>> r()() method called! >>> del c >>> gc.collect() 0 >>> r() >>>
3.4 新版功能.
-
class
weakref.finalize(obj, func, *args, **kwargs)? 返回一個(gè)可調(diào)用的終結(jié)器對象,該對象將在 obj 作為垃圾回收時(shí)被調(diào)用。 與普通的弱引用不同,終結(jié)器將總是存活,直到引用對象被回收,這極大地簡化了生存期管理。
終結(jié)器總是被視為 存活 直到它被調(diào)用(顯式調(diào)用或在垃圾回收時(shí)隱式調(diào)用),調(diào)用之后它將 死亡。 調(diào)用存活的終結(jié)器將返回
func(*arg, **kwargs)的求值結(jié)果,而調(diào)用死亡的終結(jié)器將返回None。在垃圾收集期間由終結(jié)器回調(diào)所引發(fā)異常將顯示于標(biāo)準(zhǔn)錯誤輸出,但無法被傳播。 它們會按與對象的
__del__()方法或弱引用的回調(diào)所引發(fā)異常相同的方式被處理。當(dāng)程序退出時(shí),剩余的存活終結(jié)器會被調(diào)用,除非它們的
atexit屬性已被設(shè)為假值。 它們會按與創(chuàng)建時(shí)相反的順序被調(diào)用。終結(jié)器在 interpreter shutdown 的后期絕不會發(fā)起調(diào)用其回調(diào)函數(shù),此時(shí)模塊全局變量很可能已被替換為
None。-
__call__()? 如果 self 為存活狀態(tài)則將其標(biāo)記為已死亡,并返回調(diào)用
func(*args, **kwargs)的結(jié)果。 如果 self 已死亡則返回None。
-
alive? 如果終結(jié)器為存活狀態(tài)則該特征屬性為真值,否則為假值。
-
atexit? 一個(gè)可寫的布爾型特征屬性,默認(rèn)為真值。 當(dāng)程序退出時(shí),它會調(diào)用所有
atexit為真值的剩余存活終結(jié)器。 它們會按與創(chuàng)建時(shí)相反的順序被調(diào)用。
注解
很重要的一點(diǎn)是確保 func, args 和 kwargs 不擁有任何對 obj 的引用,無論是直接的或是間接的,否則的話 obj 將永遠(yuǎn)不會被作為垃圾回收。 特別地,func 不應(yīng)當(dāng)是 obj 的一個(gè)綁定方法。
3.4 新版功能.
-
-
weakref.ReferenceType? 弱引用對象的類型對象。
-
weakref.ProxyType? 不可調(diào)用的對象的代理的類型對象。
-
weakref.CallableProxyType? 可調(diào)用對象的代理的類型對象。
-
weakref.ProxyTypes? 包含所有代理的類型對象的序列。 這可以用于更方便地檢測一個(gè)對象是否是代理,而不必依賴于兩種代理對象的名稱。
參見
- PEP 205 - 弱引用
此特性的提議和理由,包括早期實(shí)現(xiàn)的鏈接和其他語言中類似特性的相關(guān)信息。
弱引用對象?
弱引用對象沒有 ref.__callback__ 以外的方法和屬性。 一個(gè)弱引用對象如果存在,就允許通過調(diào)用它來獲取引用:
>>> import weakref
>>> class Object:
... pass
...
>>> o = Object()
>>> r = weakref.ref(o)
>>> o2 = r()
>>> o is o2
True
如果引用已不存在,則調(diào)用引用對象將返回 None:
>>> del o, o2
>>> print(r())
None
檢測一個(gè)弱引用對象是否仍然存在應(yīng)該使用表達(dá)式 ref() is not None。 通常,需要使用引用對象的應(yīng)用代碼應(yīng)當(dāng)遵循這樣的模式:
# r is a weak reference object
o = r()
if o is None:
# referent has been garbage collected
print("Object has been deallocated; can't frobnicate.")
else:
print("Object is still live!")
o.do_something_useful()
使用單獨(dú)的“存活”測試會在多線程應(yīng)用中制造競爭條件;其他線程可能導(dǎo)致某個(gè)弱引用在該弱引用被調(diào)用前就失效;上述的寫法在多線程應(yīng)用和單線程應(yīng)用中都是安全的。
特別版本的 ref 對象可以通過子類化來創(chuàng)建。 在 WeakValueDictionary 的實(shí)現(xiàn)中就使用了這種方式來減少映射中每個(gè)條目的內(nèi)存開銷。 這對于將附加信息關(guān)聯(lián)到引用的情況最為適用,但也可以被用于在調(diào)用中插入額外處理來提取引用。
這個(gè)例子演示了如何將 ref 的一個(gè)子類用于存儲有關(guān)對象的附加信息并在引用被訪問時(shí)影響其所返回的值:
import weakref
class ExtendedRef(weakref.ref):
def __init__(self, ob, callback=None, **annotations):
super(ExtendedRef, self).__init__(ob, callback)
self.__counter = 0
for k, v in annotations.items():
setattr(self, k, v)
def __call__(self):
"""Return a pair containing the referent and the number of
times the reference has been called.
"""
ob = super(ExtendedRef, self).__call__()
if ob is not None:
self.__counter += 1
ob = (ob, self.__counter)
return ob
示例?
這個(gè)簡單的例子演示了一個(gè)應(yīng)用如何使用對象 ID 來提取之前出現(xiàn)過的對象。 然后對象的 ID 可以在其它數(shù)據(jù)結(jié)構(gòu)中使用,而無須強(qiáng)制對象保持存活,但處于存活狀態(tài)的對象也仍然可以通過 ID 來提取。
import weakref
_id2obj_dict = weakref.WeakValueDictionary()
def remember(obj):
oid = id(obj)
_id2obj_dict[oid] = obj
return oid
def id2obj(oid):
return _id2obj_dict[oid]
終結(jié)器對象?
使用 finalize 的主要好處在于它能更簡便地注冊回調(diào)函數(shù),而無須保留所返回的終結(jié)器對象。 例如
>>> import weakref
>>> class Object:
... pass
...
>>> kenny = Object()
>>> weakref.finalize(kenny, print, "You killed Kenny!")
<finalize object at ...; for 'Object' at ...>
>>> del kenny
You killed Kenny!
終結(jié)器也可以被直接調(diào)用。 但是終結(jié)器最多只能對回調(diào)函數(shù)發(fā)起一次調(diào)用。
>>> def callback(x, y, z):
... print("CALLBACK")
... return x + y + z
...
>>> obj = Object()
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
>>> assert f.alive
>>> assert f() == 6
CALLBACK
>>> assert not f.alive
>>> f() # callback not called because finalizer dead
>>> del obj # callback not called because finalizer dead
你可以使用 detach() 方法來注銷一個(gè)終結(jié)器。 該方法將銷毀終結(jié)器并返回其被創(chuàng)建時(shí)傳給構(gòu)造器的參數(shù)。
>>> obj = Object()
>>> f = weakref.finalize(obj, callback, 1, 2, z=3)
>>> f.detach()
(<...Object object ...>, <function callback ...>, (1, 2), {'z': 3})
>>> newobj, func, args, kwargs = _
>>> assert not f.alive
>>> assert newobj is obj
>>> assert func(*args, **kwargs) == 6
CALLBACK
除非你將 atexit 屬性設(shè)為 False,否則終結(jié)器在程序退出時(shí)如果仍然存活就將被調(diào)用。 例如
>>> obj = Object()
>>> weakref.finalize(obj, print, "obj dead or exiting")
<finalize object at ...; for 'Object' at ...>
>>> exit()
obj dead or exiting
比較終結(jié)器與 __del__() 方法?
假設(shè)我們想創(chuàng)建一個(gè)類,用它的實(shí)例來代表臨時(shí)目錄。 當(dāng)以下事件中的某一個(gè)發(fā)生時(shí),這個(gè)目錄應(yīng)當(dāng)與其內(nèi)容一起被刪除:
對象被作為垃圾回收,
對象的
remove()方法被調(diào)用,或程序退出。
我們可以嘗試使用 __del__() 方法來實(shí)現(xiàn)這個(gè)類,如下所示:
class TempDir:
def __init__(self):
self.name = tempfile.mkdtemp()
def remove(self):
if self.name is not None:
shutil.rmtree(self.name)
self.name = None
@property
def removed(self):
return self.name is None
def __del__(self):
self.remove()
從 Python 3.4 開始,__del__() 方法不會再阻止循環(huán)引用被作為垃圾回收,并且模塊全局變量在 interpreter shutdown 期間不會被強(qiáng)制設(shè)為 None。 因此這段代碼在 CPython 上應(yīng)該會正常運(yùn)行而不會出現(xiàn)任何問題。
然而,__del__() 方法的處理會嚴(yán)重地受到具體實(shí)現(xiàn)的影響,因?yàn)樗蕾囉诮忉屍骼厥諏?shí)現(xiàn)方式的內(nèi)部細(xì)節(jié)。
更健壯的替代方式可以是定義一個(gè)終結(jié)器,只引用它所需要的特定函數(shù)和對象,而不是獲取對整個(gè)對象狀態(tài)的訪問權(quán):
class TempDir:
def __init__(self):
self.name = tempfile.mkdtemp()
self._finalizer = weakref.finalize(self, shutil.rmtree, self.name)
def remove(self):
self._finalizer()
@property
def removed(self):
return not self._finalizer.alive
像這樣定義后,我們的終結(jié)器將只接受一個(gè)對其完成正確清理目錄任務(wù)所需細(xì)節(jié)的引用。 如果對象一直未被作為垃圾回收,終結(jié)器仍會在退出時(shí)被調(diào)用。
基于弱引用的終結(jié)器還具有另一項(xiàng)優(yōu)勢,就是它們可被用來為定義由第三方控制的類注冊終結(jié)器,例如當(dāng)一個(gè)模塊被卸載時(shí)運(yùn)行特定代碼:
import weakref, sys
def unloading_module():
# implicit reference to the module globals from the function body
weakref.finalize(sys.modules[__name__], unloading_module)
注解
如果當(dāng)程序退出時(shí)你恰好在守護(hù)線程中創(chuàng)建終結(jié)器對象,則有可能該終結(jié)器不會在退出時(shí)被調(diào)用。 但是,在一個(gè)守護(hù)線程中 atexit.register(), try: ... finally: ... 和 with: ... 同樣不能保證執(zhí)行清理。
