我遇到了Python3.2的问题。如果一个类从父类中装饰一个函数并且还具有析构函数,则该类的实例永远不会被垃圾回收。
这里是一些示例代码,说明了此问题:
def super_simple_decorator(func):
def f(*args, **kwds):
return func(*args, **kwds)
return f
class Parent():
def foo(self):
pass
class Child(Parent):
def __del__(self):
print('In Child.__del__')
def __init__(self):
self.foo = super_simple_decorator(self.foo)
x = Child()
del x
import gc
_ = gc.collect()
print(gc.garbage)
如果您愿意,也可以在运行时在装饰器中添加猴子补丁,然后看到相同的内容:
class Garbage():
def foo(self):
pass
def __del__(self):
print('In Garbage.__del__')
g=Garbage()
g.foo = super_simple_decorator(g.foo)
del g
在每种情况下,都存在未收集的垃圾,这大概是因为在修饰方法中有对self
的绑定引用。
在这一点上,升级到Python3.4并不是我真正的选择,因此我正在寻找一种方法来让诸如此类的对象被垃圾回收。
python大神给出的解决方案
导致此问题的不是装饰器。实际上,您将方法存储在它们所绑定的实例上。装饰器只是这里的手段,而不是实际的原因。
方法在__self__
中保存对实例的引用,然后通过将方法存储在带有装饰器对象的闭包中并返回到self.foo
的方式来创建循环引用。不要那样做Python 3.3和更早版本不会使用__del__
方法来垃圾回收带有对象的循环引用。
解开方法并存储原始功能:
self.foo = super_simple_decorator(self.foo.__func__)
foo
将不再绑定,但是方法仅在类上(而不是实例)查找时才绑定。
或者实际上在类级别上应用装饰器:
class Child(Parent):
def __del__(self):
print('In Child.__del__')
foo = super_simple_decorator(Parent.foo)
如果两者都不是选项,请使用弱引用来跟踪实例,而不是引用方法,然后根据需要重新绑定:
import weakref
def super_simple_decorator(method):
instance = weakref.ref(method.__self__)
func = method.__func__
def f(*args, **kwds):
self = instance() # can return None
return func(self, *args, **kwds)
return f