setattr,对象删除和循环垃圾收集

时间:2012-03-22 17:27:25

标签: 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 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会认识到我正在运行python 2.x版本。更具体地说,此代码在python 2.7.1安装程序上运行。

此代码输出以下内容:

----------Test on A
Hello, World!
----------Test on B
Hello, World!
I'm dying

令人惊讶的是,A对象未被删除。我可以理解为什么,因为setattr中的__init__语句产生循环引用。但这个似乎很容易解决。

最后,这个页面在python documentation (Supporting Cyclic Garbage Collection)中表明可以处理这种循环引用。

我想知道:

  • 为什么我永远不会通过__del__课程中的A方法?
  • 如果我对循环引用的诊断是好的,为什么我的object子类不支持循环垃圾收集?
  • 最后,如果我真的希望通过setattr,如何处理这种__del__

注意:在A setattr如果{{1}}指向我模块的其他方法,则没有问题。

2 个答案:

答案 0 :(得分:1)

事实1

实例方法通常存储在中。解释器首先在实例__dict__中查找它们,它失败了,然后查看成功的类。

A中动态设置__init__的实例方法时,可以在实例字典中创建对它的引用。此引用是循环的,因此引用计数器永远不会为零,并且引用计数器不会清除A

>>> class A(object):
...     def _test(self): pass
...     def __init__(self):
...             self.test = self._test
... 
>>> a = A()
>>> a.__dict__['test'].im_self

事实2

垃圾收集器是Python用来处理循环引用的东西。不幸的是,它无法使用__del__方法处理对象,因为通常它无法确定调用它们的安全顺序。相反,它只是将所有这些对象放在gc.garbage中。然后你可以去那里打破周期,这样他们就可以被释放。来自docs

gc.garbage
     

收集器发现无法访问但可能无法访问的对象列表   不被释放(无法收集的物品)。默认情况下,此列表仅包含   具有__del__()方法的对象。具有__del__()方法的对象   并且是参考循环的一部分导致整个参考循环   无法收集,包括不一定的对象   在循环中但只能从它到达。 Python不收集这样的   自动循环,因为一般来说,Python不可能   猜测运行__del__()方法的安全顺序。如果你   知道一个安全的命令,你可以通过检查垃圾来强制解决问题   列表,并明确地打破由于您的对象内的周期   名单。请注意,这些物体即使如此也能保持活力   在垃圾清单中,所以它们也应该从垃圾中删除。   例如,在打破周期后,执行del gc.garbage[:]清空   名单。通过不创建周期来避免这个问题通常会更好   包含__del__()方法的对象,可以检查garbage   在这种情况下,要验证没有创建这样的周期。

因此

如果您希望对象进行垃圾回收,请不要使用__del__方法对对象进行循环引用。

答案 1 :(得分:0)

您应该仔细阅读documentation on the __del__方法 - 具体而言,使用__del__方法的对象改变收集器工作方式的部分。

gc module提供了一些钩子,您可以自己清理它。

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