weakref
--- 弱引用?
源碼: Lib/weakref.py
weakref
模塊允許Python程序員創(chuàng )建對象的 weak references 。
在下文中,術(shù)語(yǔ) referent 表示由弱引用引用的對象。
對對象的弱引用不能保證對象存活:當對像的引用只剩弱引用時(shí), garbage collection 可以銷(xiāo)毀引用并將其內存重用于其他內容。但是,在實(shí)際銷(xiāo)毀對象之前,即使沒(méi)有強引用,弱引用也一直能返回該對象。
弱引用的主要用途是實(shí)現保存大對象的高速緩存或映射,但又不希望大對象僅僅因為它出現在高速緩存或映射中而保持存活。
例如,如果您有許多大型二進(jìn)制圖像對象,則可能希望將名稱(chēng)與每個(gè)對象關(guān)聯(lián)起來(lái)。如果您使用Python字典將名稱(chēng)映射到圖像,或將圖像映射到名稱(chēng),則圖像對象將保持活動(dòng)狀態(tài),因為它們在字典中顯示為值或鍵。 weakref
模塊提供的 WeakKeyDictionary
和 WeakValueDictionary
類(lèi)可以替代Python字典,使用弱引用來(lái)構造映射,這些映射不會(huì )僅僅因為它們出現在映射對象中而使對象保持存活。例如,如果一個(gè)圖像對象是 WeakValueDictionary
中的值,那么當對該圖像對象的剩余引用是弱映射對象所持有的弱引用時(shí),垃圾回收可以回收該對象并將其在弱映射對象中相應的條目刪除。
WeakKeyDictionary
和 WeakValueDictionary
在它們的實(shí)現中使用弱引用,在弱引用上設置回調函數,當鍵或值被垃圾回收回收時(shí)通知弱字典。 WeakSet
實(shí)現了 set
接口,但像 WeakKeyDictionary
一樣,只持有其元素的弱引用。
finalize
提供了注冊一個(gè)對象被垃圾收集時(shí)要調用的清理函數的方式。這比在原始弱引用上設置回調函數更簡(jiǎn)單,因為模塊會(huì )自動(dòng)確保對象被回收前終結器一直保持存活。
這些弱容器類(lèi)型之一或者 finalize
就是大多數程序所需要的 - 通常不需要直接創(chuàng )建自己的弱引用。weakref
模塊暴露了低級機制,以便于高級用途。
Not all objects can be weakly referenced. Objects which support weak references include class instances, functions written in Python (but not in C), instance methods, sets, frozensets, some file objects, generators, type objects, sockets, arrays, deques, regular expression pattern objects, and code objects.
在 3.2 版更改: 添加了對thread.lock,threading.Lock和代碼對象的支持。
幾個(gè)內建類(lèi)型如 list
和 dict
不直接支持弱引用,但可以通過(guò)子類(lèi)化添加支持:
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.
When __slots__
are defined for a given type, weak reference support is
disabled unless a '__weakref__'
string is also present in the sequence of
strings in the __slots__
declaration.
See __slots__ documentation for details.
- class weakref.ref(object[, callback])?
返回對 對象 的弱引用。如果原始對象仍然存活,則可以通過(guò)調用引用對象來(lái)檢索原始對象;如果引用的原始對象不再存在,則調用引用對象將得到
None
。如果提供了 回調 而且值不是None
,并且返回的弱引用對象仍然存活,則在對象即將終結時(shí)將調用回調;弱引用對象將作為回調的唯一參數傳遞;指示物將不再可用。許多弱引用也允許針對相同對象來(lái)構建。 為每個(gè)弱引用注冊的回調將按從最近注冊的回調到最早注冊的回調的順序被調用。
回調所引發(fā)的異常將記錄于標準錯誤輸出,但無(wú)法被傳播;它們會(huì )按與對象的
__del__()
方法所引發(fā)的異常相同的方式被處理。如果 object 可哈希,則弱引用也為 hashable。 即使在 object 被刪除之后它們仍將保持其哈希值。 如果
hash()
在 object 被刪除之后才首次被調用,則該調用將引發(fā)TypeError
。弱引用支持相等檢測,但不支持排序比較。 如果被引用對象仍然存在,兩個(gè)引用具有與它們的被引用對象一致的相等關(guān)系(無(wú)論 callback 是否相同)。 如果刪除了任一被引用對象,則僅在兩個(gè)引用對象為同一對象時(shí)兩者才相等。
這是一個(gè)可子類(lèi)化的類(lèi)型而非一個(gè)工廠(chǎng)函數。
- __callback__?
這個(gè)只讀屬性會(huì )返回當前關(guān)聯(lián)到弱引用的回調。 如果回調不存在或弱引用的被引用對象已不存在,則此屬性的值為
None
。
在 3.4 版更改: 添加了
__callback__
屬性。
- weakref.proxy(object[, callback])?
返回 object 的一個(gè)使用弱引用的代理。 此函數支持在大多數上下文中使用代理,而不要求顯式地對所使用的弱引用對象解除引用。 返回的對象類(lèi)型將為
ProxyType
或CallableProxyType
,具體取決于 object 是否可調用。 Proxy 對象不是 hashable 對象,無(wú)論被引用對象是否可哈希;這可避免與它們的基本可變性質(zhì)相關(guān)的多種問(wèn)題,并可防止它們被用作字典鍵。 callback 與ref()
函數的同名形參含義相同。在 3.8 版更改: 擴展代理對象所支持的運算符,包括矩陣乘法運算符
@
和@=
。
- weakref.getweakrefcount(object)?
返回指向 object 的弱引用和代理的數量。
- weakref.getweakrefs(object)?
返回由指向 object 的所有弱引用和代理構成的列表。
- class weakref.WeakKeyDictionary([dict])?
弱引用鍵的映射類(lèi)。 當不再存在對鍵的強引用時(shí),字典中的條目將被丟棄。 這可被用來(lái)將額外數據關(guān)聯(lián)到一個(gè)應用中其他部分所擁有的對象而無(wú)需在那些對象中添加屬性。 這對于重載了屬性訪(fǎng)問(wèn)的對象來(lái)說(shuō)特別有用。
在 3.9 版更改: 增加了對
|
和|=
運算符的支持,相關(guān)說(shuō)明見(jiàn) PEP 584。
WeakKeyDictionary
對象具有一個(gè)額外方法可以直接公開(kāi)內部引用。 這些引用不保證在它們被使用時(shí)仍然保持“存活”,因此這些引用的調用結果需要在使用前進(jìn)行檢測。 此方法可用于避免創(chuàng )建會(huì )導致垃圾回收器將保留鍵超出實(shí)際需要時(shí)長(cháng)的引用。
- WeakKeyDictionary.keyrefs()?
返回包含對鍵的弱引用的可迭代對象。
- class weakref.WeakValueDictionary([dict])?
弱引用值的映射類(lèi)。 當不再存在對該值的強引用時(shí),字典中的條目將被丟棄。
在 3.9 版更改: 增加了對
|
和|=
運算符的支持,相關(guān)說(shuō)明見(jiàn) PEP 584。
WeakValueDictionary
對象具有一個(gè)額外方法,此方法存在與 WeakKeyDictionary
對象的 keyrefs()
方法相同的問(wèn)題。
- WeakValueDictionary.valuerefs()?
返回包含對值的弱引用的可迭代對象。
- class weakref.WeakSet([elements])?
保持對其元素弱引用的集合類(lèi)。 當不再有對某個(gè)元素的強引用時(shí)元素將被丟棄。
- class weakref.WeakMethod(method)?
一個(gè)模擬對綁定方法(即在類(lèi)中定義并在實(shí)例中查找的方法)進(jìn)行弱引用的自定義
ref
子類(lèi)。 由于綁定方法是臨時(shí)性的,標準弱引用無(wú)法保持它。WeakMethod
包含特別代碼用來(lái)重新創(chuàng )建綁定方法,直到對象或初始函數被銷(xiāo)毀:>>> 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è)可調用的終結器對象,該對象將在 obj 作為垃圾回收時(shí)被調用。 與普通的弱引用不同,終結器將總是存活,直到引用對象被回收,這極大地簡(jiǎn)化了生存期管理。
終結器總是被視為 存活 直到它被調用(顯式調用或在垃圾回收時(shí)隱式調用),調用之后它將 死亡。 調用存活的終結器將返回
func(*arg, **kwargs)
的求值結果,而調用死亡的終結器將返回None
。在垃圾收集期間由終結器回調所引發(fā)異常將顯示于標準錯誤輸出,但無(wú)法被傳播。 它們會(huì )按與對象的
__del__()
方法或弱引用的回調所引發(fā)異常相同的方式被處理。當程序退出時(shí),剩余的存活終結器會(huì )被調用,除非它們的
atexit
屬性已被設為假值。 它們會(huì )按與創(chuàng )建時(shí)相反的順序被調用。終結器在 interpreter shutdown 的后期絕不會(huì )發(fā)起調用其回調函數,此時(shí)模塊全局變量很可能已被替換為
None
。- alive?
如果終結器為存活狀態(tài)則該特征屬性為真值,否則為假值。
- atexit?
一個(gè)可寫(xiě)的布爾型特征屬性,默認為真值。 當程序退出時(shí),它會(huì )調用所有
atexit
為真值的剩余存活終結器。 它們會(huì )按與創(chuàng )建時(shí)相反的順序被調用。
備注
很重要的一點(diǎn)是確保 func, args 和 kwargs 不擁有任何對 obj 的引用,無(wú)論是直接的或是間接的,否則的話(huà) obj 將永遠不會(huì )被作為垃圾回收。 特別地,func 不應當是 obj 的一個(gè)綁定方法。
3.4 新版功能.
- weakref.ReferenceType?
弱引用對象的類(lèi)型對象。
- weakref.ProxyType?
不可調用對象的代理的類(lèi)型對象。
- weakref.CallableProxyType?
可調用對象的代理的類(lèi)型對象。
- weakref.ProxyTypes?
包含所有代理的類(lèi)型對象的序列。 這可以用于更方便地檢測一個(gè)對象是否是代理,而不必依賴(lài)于兩種代理對象的名稱(chēng)。
參見(jiàn)
- PEP 205 - 弱引用
此特性的提議和理由,包括早期實(shí)現的鏈接和其他語(yǔ)言中類(lèi)似特性的相關(guān)信息。
弱引用對象?
弱引用對象沒(méi)有 ref.__callback__
以外的方法和屬性。 一個(gè)弱引用對象如果存在,就允許通過(guò)調用它來(lái)獲取引用:
>>> import weakref
>>> class Object:
... pass
...
>>> o = Object()
>>> r = weakref.ref(o)
>>> o2 = r()
>>> o is o2
True
如果引用已不存在,則調用引用對象將返回 None
:
>>> del o, o2
>>> print(r())
None
檢測一個(gè)弱引用對象是否仍然存在應該使用表達式 ref() is not None
。 通常,需要使用引用對象的應用代碼應當遵循這樣的模式:
# 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()
使用單獨的“存活”測試會(huì )在多線(xiàn)程應用中制造競爭條件;其他線(xiàn)程可能導致某個(gè)弱引用在該弱引用被調用前就失效;上述的寫(xiě)法在多線(xiàn)程應用和單線(xiàn)程應用中都是安全的。
特別版本的 ref
對象可以通過(guò)子類(lèi)化來(lái)創(chuàng )建。 在 WeakValueDictionary
的實(shí)現中就使用了這種方式來(lái)減少映射中每個(gè)條目的內存開(kāi)銷(xiāo)。 這對于將附加信息關(guān)聯(lián)到引用的情況最為適用,但也可以被用于在調用中插入額外處理來(lái)提取引用。
這個(gè)例子演示了如何將 ref
的一個(gè)子類(lèi)用于存儲有關(guān)對象的附加信息并在引用被訪(fǎng)問(wèn)時(shí)影響其所返回的值:
import weakref
class ExtendedRef(weakref.ref):
def __init__(self, ob, callback=None, /, **annotations):
super().__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().__call__()
if ob is not None:
self.__counter += 1
ob = (ob, self.__counter)
return ob
示例?
這個(gè)簡(jiǎn)單的例子演示了一個(gè)應用如何使用對象 ID 來(lái)提取之前出現過(guò)的對象。 然后對象的 ID 可以在其它數據結構中使用,而無(wú)須強制對象保持存活,但處于存活狀態(tài)的對象也仍然可以通過(guò) ID 來(lái)提取。
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]
終結器對象?
使用 finalize
的主要好處在于它能更簡(jiǎn)便地注冊回調函數,而無(wú)須保留所返回的終結器對象。 例如
>>> 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!
終結器也可以被直接調用。 但是終結器最多只能對回調函數發(fā)起一次調用。
>>> 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()
方法來(lái)注銷(xiāo)一個(gè)終結器。 該方法將銷(xiāo)毀終結器并返回其被創(chuàng )建時(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
屬性設為 False
,否則終結器在程序退出時(shí)如果仍然存活就將被調用。 例如
>>> obj = Object()
>>> weakref.finalize(obj, print, "obj dead or exiting")
<finalize object at ...; for 'Object' at ...>
>>> exit()
obj dead or exiting
比較終結器與 __del__()
方法?
假設我們想創(chuàng )建一個(gè)類(lèi),用它的實(shí)例來(lái)代表臨時(shí)目錄。 當以下事件中的某一個(gè)發(fā)生時(shí),這個(gè)目錄應當與其內容一起被刪除:
對象被作為垃圾回收,
對象的
remove()
方法被調用,或程序退出。
我們可以嘗試使用 __del__()
方法來(lái)實(shí)現這個(gè)類(lèi),如下所示:
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 開(kāi)始,__del__()
方法不會(huì )再阻止循環(huán)引用被作為垃圾回收,并且模塊全局變量在 interpreter shutdown 期間不會(huì )被強制設為 None
。 因此這段代碼在 CPython 上應該會(huì )正常運行而不會(huì )出現任何問(wèn)題。
然而,__del__()
方法的處理會(huì )嚴重地受到具體實(shí)現的影響,因為它依賴(lài)于解釋器垃圾回收實(shí)現方式的內部細節。
更健壯的替代方式可以是定義一個(gè)終結器,只引用它所需要的特定函數和對象,而不是獲取對整個(gè)對象狀態(tài)的訪(fǎng)問(wè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
像這樣定義后,我們的終結器將只接受一個(gè)對其完成正確清理目錄任務(wù)所需細節的引用。 如果對象一直未被作為垃圾回收,終結器仍會(huì )在退出時(shí)被調用。
基于弱引用的終結器還具有另一項優(yōu)勢,就是它們可被用來(lái)為定義由第三方控制的類(lèi)注冊終結器,例如當一個(gè)模塊被卸載時(shí)運行特定代碼:
import weakref, sys
def unloading_module():
# implicit reference to the module globals from the function body
weakref.finalize(sys.modules[__name__], unloading_module)
備注
如果當程序退出時(shí)你恰好在守護線(xiàn)程中創(chuàng )建終結器對象,則有可能該終結器不會(huì )在退出時(shí)被調用。 但是,在一個(gè)守護線(xiàn)程中 atexit.register()
, try: ... finally: ...
和 with: ...
同樣不能保證執行清理。