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)
可以看到此时就支持弱引用了。
原文:https://www.cnblogs.com/traditional/p/11870281.html