weakref:对象的弱引用
介紹
weakref支持對(duì)象的弱引用,正常的引用會(huì)增加對(duì)象的引用計(jì)數(shù),并避免它被垃圾回收。但結(jié)果并不是總和期望的那樣,比如有時(shí)候可能會(huì)出現(xiàn)一個(gè)循環(huán)引用,或者有時(shí)候需要內(nèi)存時(shí)可能要?jiǎng)h除對(duì)象的緩存。而弱引用(weak reference)是一個(gè)不會(huì)增加引用計(jì)數(shù)的對(duì)象句柄
引用
import weakref
'''
對(duì)象的弱引用要通過ref類來管理。要獲取原對(duì)象,可以調(diào)用引用對(duì)象
'''
class RefObject:
def __del__(self):
print("del executed")
obj = RefObject()
# 創(chuàng)建弱引用
r = weakref.ref(obj)
print("obj:", obj) # obj: <__main__.RefObject object at 0x0000000002964470>
# 顯示關(guān)聯(lián)RefObject
print("ref:", r) # ref: <weakref at 0x000000000051BA48; to 'RefObject' at 0x0000000002964470>
# 引用r加上(),等價(jià)于obj,因此得到RefObject的實(shí)例對(duì)象
print("ref()", r()) # ref() <__main__.RefObject object at 0x0000000002964470>
# 刪除obj執(zhí)行析構(gòu)函數(shù)
del obj # del executed
# 之前說過調(diào)用r()等價(jià)于調(diào)用obj,但是obj被刪除了,所以返回None
# 從這里返回None也能看出這個(gè)弱引用是不會(huì)增加引用計(jì)數(shù)的
print("r():", r()) # r(): None
引用回調(diào)
import weakref
'''
ref構(gòu)造函數(shù)可以接受一個(gè)可選的回調(diào)函數(shù),刪除引用所指向的對(duì)象時(shí)就會(huì)調(diào)用這個(gè)回調(diào)函數(shù)
'''
class RefObject:
def __del__(self):
print("del executed")
def callback(reference):
print(f"callback : {reference}")
obj = RefObject()
r = weakref.ref(obj, callback)
'''
當(dāng)引用所引用的原對(duì)象"死亡"時(shí),這個(gè)回調(diào)會(huì)接受這個(gè)引用對(duì)象作為參數(shù)。
這種特性的一種用法就是從緩存中刪除弱引用對(duì)象。
'''
print("obj:", obj) # obj: <__main__.RefObject object at 0x0000000002964630>
print("ref:", r) # ref: <weakref at 0x0000000001D2BA48; to 'RefObject' at 0x0000000002964630>
print("ref()", r()) # ref() <__main__.RefObject object at 0x0000000002964630>
del obj # 刪除引用指向的對(duì)象
"""
del executed
callback : <weakref at 0x0000000001D2BA48; dead> 刪除obj,執(zhí)行回調(diào),顯示dead
"""
print("r():", r()) # r(): None
最終化對(duì)象
import weakref
'''
清理弱引用時(shí)要對(duì)資源完成更健壯的管理,可以使用finalize將回調(diào)與對(duì)象關(guān)聯(lián)。
finalize實(shí)例會(huì)一直保留(直到所關(guān)聯(lián)的對(duì)象被刪除),即使沒有保留最終化對(duì)象的引用
'''
class RefObj:
def __del__(self):
print("xxx")
def on_finalize(*args):
print(f"on_finalize: {args}")
obj = RefObj()
weakref.finalize(obj, on_finalize, "callback的參數(shù)")
del obj
'''
xxx
on_finalize: ('callback的參數(shù)',)
'''
# finalize的參數(shù)包括要跟蹤的對(duì)象,對(duì)象被垃圾回收時(shí)要調(diào)用的callback,以及參數(shù)(可以是位置參數(shù),也可以是關(guān)鍵字參數(shù))
# finalize實(shí)例對(duì)象還有一個(gè)atexit屬性,用來控制程序退出時(shí)是否調(diào)用這個(gè)回調(diào)(如果還未調(diào)用)
obj1 = RefObj()
f = weakref.finalize(obj1, on_finalize, "callback的參數(shù)")
# 默認(rèn)是調(diào)用回調(diào),但是將atexit設(shè)置為False會(huì)禁用這種行為
f.atexit = False
'''
不會(huì)有任何的輸出,注意:這里我雖然沒有顯示的刪除obj1,但也能夠說明結(jié)論
因?yàn)樵趂.atexit=True的情況下,即使不刪除也依舊會(huì)執(zhí)行callback。
原因是即使你不手動(dòng)刪除,但是對(duì)象已經(jīng)被創(chuàng)建出來了,而程序結(jié)束的那一刻,也會(huì)執(zhí)行析構(gòu)函數(shù)的。因?yàn)閷?duì)象總是要回收的,即使你不調(diào)用del,那么程序執(zhí)行完畢的時(shí)候也會(huì)自動(dòng)調(diào)用。
所以默認(rèn)f.atexit = True是會(huì)打印的,但是現(xiàn)在沒有打印,所以確實(shí)被禁用了
'''
import weakref
'''
如果向finalize實(shí)例提供一個(gè)跟蹤對(duì)象的引用,這便會(huì)導(dǎo)致一個(gè)引用被保留,所以這個(gè)對(duì)象永遠(yuǎn)不會(huì)被垃圾回收
'''
class RefObj:
def __del__(self):
print("xxx")
def on_finalize(*args):
print(f"on_finalize: {args}")
obj = RefObj()
obj_id = id(obj)
# 這里我將obj實(shí)例作為參數(shù)傳進(jìn)去了,這樣的后果就是obj不會(huì)被回收,即使你刪除了
f = weakref.finalize(obj, on_finalize, obj)
f.atexit = False
# 刪除obj,讓obj不再指向之前的對(duì)象
del obj
import gc
# 獲取所有的對(duì)象
for o in gc.get_objects():
if id(o) == obj_id:
# 結(jié)果發(fā)現(xiàn)真的沒有被回收,因?yàn)橐貌恢筼bj一個(gè),還有其它人在用
print("found uncollected object in gc") # found uncollected object in gc
代理
import weakref
'''
有時(shí)候使用代理比使用弱引用更方便。使用代理可以像使用原對(duì)象一樣,而且不要求在訪問對(duì)象之前先調(diào)用代理。
這說明,可以將代理傳遞到一個(gè)庫(kù),而這個(gè)庫(kù)并不知道它接收的是一個(gè)代理而不是真正的一個(gè)對(duì)象。
'''
class RefObj:
def __init__(self, name):
self.name = name
def __del__(self):
print("xxx")
obj = RefObj("my obj")
r = weakref.ref(obj)
p = weakref.proxy(obj)
# 可以看到引用加上()才相當(dāng)于原來的對(duì)象
# 而代理不需要,直接和原來的對(duì)象保持一致
print("via obj:", obj.name) # via obj: my obj
print("via ref:", r().name) # via ref: my obj
print("via proxy:", p.name) # via proxy: my obj
del obj # xxx
try:
# 刪除對(duì)象之后,再調(diào)用引用,打印為None
print(r()) # None
# 但是如果調(diào)用代理的話,則會(huì)拋出一個(gè)ReferenceError
print(p)
except Exception as e:
print(e) # weakly-referenced object no longer exists
自定義類指定弱引用
當(dāng)我們自定義一個(gè)類的時(shí)候,如果為了省內(nèi)存,那么會(huì)不使用__dict__屬性,因?yàn)槊恳粋€(gè)類或者實(shí)例都會(huì)有一個(gè)自己的屬性字典__dict__,而我們知道字典使用的是哈希表,這是一個(gè)使用空間換時(shí)間的數(shù)據(jù)結(jié)構(gòu),因此如果想省內(nèi)存的話,那么我們通常的做法是指定__slots__屬性,這樣就不會(huì)再有__dict__屬性了。
class A:
__slots__ = ('name', 'age')
def __init__(self, name, age):
# 此時(shí)在__init__里面,只能有self.name和self.age
# 這是因?yàn)槲覀冊(cè)赺_slots__里面只指定了name和age
# 因此當(dāng)我們需要省內(nèi)存、并且屬性固定的時(shí)候,可以指定__slots__屬性
self.name = name
self.age = age
def __str__(self):
return f"name is {self.name}, age is {self.age}"
if __name__ == '__main__':
import weakref
a = A("hanser", 27)
try:
r = weakref.proxy(a)
except TypeError as e:
print(e) # cannot create weak reference to 'A' object
但是我們發(fā)現(xiàn)此時(shí)這個(gè)A的實(shí)例對(duì)象是沒有辦法被弱引用的,因?yàn)槲覀冎付?code>__slots__,那么要怎么做呢?直接在__slots__里面加上一個(gè)屬性就好了。
class A:
# 多指定一個(gè)__weakref__,表示支持弱引用
__slots__ = ('name', 'age', '__weakref__')
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"name is {self.name}, age is {self.age}"
if __name__ == '__main__':
import weakref
a = A("hanser", 27)
r = weakref.proxy(a)
print(r)
可以看到此時(shí)就支持弱引用了。
總結(jié)
以上是生活随笔為你收集整理的weakref:对象的弱引用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入浅出Attribute(二)
- 下一篇: TCP/UDP对比总结