具有修饰方法和定义的_del________)的Python类不会被垃圾收集:如何解耦修饰方法?

具有修饰方法和定义的_del________)的Python类不会被垃圾收集:如何解耦修饰方法?,python,garbage-collection,decorator,python-decorators,Python,Garbage Collection,Decorator,Python Decorators,我在Python3.2上遇到了一个问题。如果一个类装饰了父类中的一个函数,并且还有一个析构函数,那么该类的实例永远不会被垃圾收集 下面是一些示例代码,说明了问题: def super_simple_decorator(func): def f(*args, **kwds): return func(*args, **kwds) return f class Parent(): def foo(self): pass class Chil

我在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)
如果您有这种倾向,您也可以在运行时在装饰器中使用monkey patch,并看到同样的情况:

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在这一点上对我来说并不是一个真正的选择,所以我正在寻找一种方法来让像这样的对象被垃圾收集。

导致这个问题的不是装饰程序。事实上,您在它们绑定到的实例上存储了一个方法。装饰者只是这里的手段,而不是真正的原因

方法在
\uuuuu self\uuuuu
中保存对实例的引用,然后通过将该方法存储在带有decorator对象的闭包中,并返回到
self.foo
中,创建了一个循环引用。不要那样做。Python3.3及之前的版本不会使用
\uuu del\uu
方法对对象的循环引用进行垃圾收集

展开方法并存储原始函数:

self.foo = super_simple_decorator(self.foo.__func__)
foo
将不再绑定,但是,仅当在类而不是实例上查找时,才会绑定方法

或者在类级别实际应用decorator:

如果两者都不是选项,请使用弱引用来跟踪实例,而不是引用方法,然后根据需要重新绑定:

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

self.foo
引用封装进去是否有帮助?我考虑过,但是它不需要用
g.foo()()
调用吗?没关系,我现在从@Martijn Pieters那里看到了答案。我将尝试一下。在类级别应用装饰器对于我不太简单的场景来说并不是一个真正的选项。
\uuuu func\uuu
选项以
TypeError结束:foo()正好接受1个参数(给定0)
weakref
选项给出了
UnboundLocalError:赋值前引用的局部变量“self”
weakref
的问题可以通过不重新使用名称
self
@TrevorWiley来解决。是的,很抱歉,现在就解决了。
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