Python setattr、对象删除和循环垃圾回收

Python setattr、对象删除和循环垃圾回收,python,garbage-collection,setattr,Python,Garbage Collection,Setattr,我想了解对象删除在python上是如何工作的。下面是一组非常简单的代码 class A(object): def __init__(self): setattr(self, "test", self._test) def _test(self): print "Hello, World!" def __del__(self): print "I'm dying!" class B(object): def

我想了解对象删除在python上是如何工作的。下面是一组非常简单的代码

class A(object):

    def __init__(self):
        setattr(self, "test", self._test)

    def _test(self):
        print "Hello, World!"

    def __del__(self):
        print "I'm dying!"

class B(object):

    def test(self):
        print "Hello, World!"

    def __del__(self):
        print "I'm dying"

print "----------Test on A"
A().test()
print "----------Test on B"
B().test()
Pythonista会意识到我正在运行Python2.x版本。更特别的是,这段代码在Python2.7.1设置上运行

此代码输出以下内容:

----------Test on A
Hello, World!
----------Test on B
Hello, World!
I'm dying
令人惊讶的是,
对象没有被删除。我可以理解为什么,因为
\uuuu init\uuuu
中的
setattr
语句生成循环引用。但这个问题似乎很容易解决

最后,在中的这一页显示,处理这种循环引用是可能的

我想知道:

  • 为什么我从来没有在
    A
    类中使用我的
    \uu del\uu
    方法
  • 如果我对循环引用的诊断是正确的,为什么我的
    对象
    子类不支持循环垃圾收集
  • 最后,如果我真的想通过
    \uuu del\uuu
    ,如何处理这种
    setattr
注意:在
A
中,如果
setattr
指向我模块的另一个方法,则没有问题。

您应该非常仔细地阅读该方法-特别是,带有
\u del\u
方法的对象改变收集器工作方式的部分

提供了一些钩子,您可以自己清理

我怀疑这里没有
\uuu del\uu
方法会导致对象被正确清理。您可以通过查看
gc.garbage
并查看
A
的实例是否存在来验证这一点。

Fact 1 实例方法通常存储在类中。解释器首先在实例
\uuuu dict\uuuu
中查找它们,该实例失败,然后查看类,该类成功

当您在
\uuu init\uuu
中动态设置
A
的实例方法时,您将在实例字典中创建对它的引用。此引用是循环的,因此refcount永远不会变为零,引用计数器也不会清除
A
up

>>> class A(object):
...     def _test(self): pass
...     def __init__(self):
...             self.test = self._test
... 
>>> a = A()
>>> a.__dict__['test'].im_self
事实2 垃圾收集器是Python用来处理循环引用的。不幸的是,它不能用
方法处理对象,因为通常它不能确定调用它们的安全顺序。相反,它只是将所有此类对象放入
gc.garbage
。然后你可以到那里去打破循环,这样它们就可以被释放。从

收集器发现无法访问但无法访问的对象的列表 无法释放(无法收集的对象)。默认情况下,此列表仅包含 具有
\uuu del\uuu()方法的对象。具有
方法的对象
是参考循环的一部分,导致整个参考循环
无法收回,包括不一定要收回的物品
在循环中,但只能从循环中访问。Python不收集这样的数据
自动循环,因为在一般情况下,Python不可能这样做
猜测运行
方法的安全顺序。如果你
如果你知道一个安全的命令,你可以通过检查垃圾来强制解决这个问题
列表中的对象,并显式中断循环
列表请注意,即使如此,由于
在垃圾列表中,所以它们也应该从垃圾中移除。
例如,在中断循环后,执行
del gc.garbage[:]
清空 列表通常最好不要创建循环来避免问题 可以检查包含具有
\uu del\uu()方法和
垃圾的对象
在这种情况下,验证是否未创建此类循环

所以
如果希望对象被垃圾收集,请不要对具有
方法的对象进行循环引用。

我怀疑
setattr
与此有关。您正在存储一个绑定方法,该方法存储对象。您还可以通过常规分配来存储它(
self.test=self.\u test
)。请尝试一下,如果它产生相同的输出,你可以简化这个问题。哦,是的,它产生相同的东西。但是,
self.test=self.\u test
调用对象的
\u setattr\u
属性。我说得对吗?是的,但是设置属性也应该这样做。我相信
setattr
甚至尊重属性。。。。启用选项cycle detector时(默认情况下,它处于启用状态)会检测到垃圾循环引用,但只有在没有涉及Python级别的
\uu del\uu()
方法的情况下才能清除垃圾循环引用。那么,在python级别使用del方法会破坏循环检测器吗?ohe:我的答案指向gc模块
垃圾
变量,您可以对该变量进行操作以打破循环。这非常烦人。事实上,我总是知道如何打破循环。但我想在对象删除时这样做,这就是为什么我们“重写”
\uuu del\uuu
语句(否则,我为什么要写这个语句;)。事实上,我可以通过调用
del gc.garbage[:]
查看这个
gc.garbage
列表,打破循环,删除对象,但是,正如我所说,我发现了这个。。。。烦人的谢谢你的聪明和快速的回答。@ohe我显然不知道你的案例的具体情况,但我认为更自然的解决办法是定义某种
.finish()
方法——你完成后调用它——它打破了循环。
gc.garbage