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