python - 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 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 对象没有被删除。我能理解为什么,因为 __init__ 中的 setattr 语句产生了循环引用。但这一个似乎很容易解决。

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

我想知道:

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

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

最佳答案

事实 1

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

当您在__init__ 中动态设置A 的实例方法时,您会在实例字典中创建对它的引用。此引用是循环的,因此引用计数永远不会变为零,引用计数器也不会清除 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

A list of objects which the collector found to be unreachable but could not be freed (uncollectable objects). By default, this list contains only objects with __del__() methods. Objects that have __del__() methods and are part of a reference cycle cause the entire reference cycle to be uncollectable, including objects not necessarily in the cycle but reachable only from it. Python doesn’t collect such cycles automatically because, in general, it isn’t possible for Python to guess a safe order in which to run the __del__() methods. If you know a safe order, you can force the issue by examining the garbage list, and explicitly breaking cycles due to your objects within the list. Note that these objects are kept alive even so by virtue of being in the garbage list, so they should be removed from garbage too. For example, after breaking cycles, do del gc.garbage[:] to empty the list. It’s generally better to avoid the issue by not creating cycles containing objects with __del__() methods, and garbage can be examined in that case to verify that no such cycles are being created.

因此

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

关于python - setattr、对象删除和循环垃圾回收,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9827392/

相关文章:

garbage-collection - Kubernetes:图像垃圾收集失败:无法找到容器/的数据

python - 覆盖的属性访问不起作用(如预期)

python - Django 1.9 URLField 删除必要的 http ://prefix

python - TensorFlow 使用 numpy 取消堆叠

JavaScript、内存泄漏、垃圾收集。尝试简单的出路?

scala - Apache Spark 中的大型 RDD [MatrixEntry] 超出了 GC 开销限制

python - __setattr__() 可以在带有 __slots__ 的类中定义吗?

Python __init__ setattr 关于参数?

python - Canopy 获取 shell 中的文件列表

python - 获取嵌入式 Python 运行时以使用当前事件的 virtualenv