3

我想了解对象删除如何在 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对象没有被删除。我可以理解为什么,因为 in 中的setattr语句__init__会产生循环引用。但这似乎很容易解决。

最后,在python 文档 (Supporting Cyclic Garbage Collection)中的这个页面显示了处理这种循环引用是可能的。

我想知道:

  • 为什么我从不__del__在课堂上通过我的方法A
  • 如果我对循环引用的诊断是好的,为什么我的object子类不支持循环垃圾收集?
  • setattr最后,如果我真的想通过,如何处理这种情况__del__

注意:A如果setattr指向我模块的另一个方法,则没有问题。

4

2 回答 2

1

事实 1

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

当您动态设置 in 的实例方法时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. 然后你可以去那里打破循环,这样它们就可以被释放。从文档

gc.garbage

收集器发现无法访问但无法释放的对象列表(无法收集的对象)。默认情况下,此列表仅包含具有__del__()方法的对象。具有__del__()方法并且属于引用循环的对象会导致整个引用循环无法收集,包括不一定在循环中但只能从循环中访问的对象。Python 不会自动收集此类循环,因为通常 Python 无法猜测运行循环的安全顺序__del__()方法。如果您知道安全顺序,则可以通过检查垃圾列表来强制执行该问题,并由于列表中的对象而显式中断循环。请注意,由于位于垃圾列表中,这些对象仍然保持活动状态,因此它们也应该从垃圾中删除。例如,在中断循环后,del gc.garbage[:]清空列表。通常最好通过不创建包含具有__del__()方法的对象的循环来避免该问题,并且garbage可以在这种情况下检查以验证没有创建此类循环。

所以

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

于 2012-03-22T17:50:06.257 回答
0

您应该仔细阅读有关该方法的文档__del____del__——特别是带有方法的对象改变收集器工作方式的部分。

gc 模块提供了一些钩子,您可以在其中自己清理它。

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

于 2012-03-22T17:39:54.443 回答