无法创建实例时调用的析构函数?

时间:2016-05-02 12:59:37

标签: python-3.x garbage-collection destructor instantiation

当试图理解一些Python概念时,我遇到了以下问题:

class A:
    def __init__(self, x):
        self.x = x

    def __del__(self):
        print("del")

a1 = A()

输出:

$ python test.py
del
Traceback (most recent call last):
  File "testdest.py", line 9, in <module>
    a1 = A()
TypeError: __init__() takes exactly 2 arguments (1 given)

错误是显而易见的(实例化时缺少参数),但我想知道为什么在拥有实例之前调用析构函数?

除非在尝试实例化时,Python甚至在调用构造函数之前创建了一种实例,并且需要在结束时进行清理?

由于self被传递给构造函数,我可以假设这个self是实例吗?这是真的,那么在调用构造函数时实例已经存在,是吗?

这是垃圾收集器的行为,可能取决于当前的实现吗?

1 个答案:

答案 0 :(得分:5)

来自Python文档:

  

永远不会明确销毁对象;然而,当他们成为   无法到达,他们可能被垃圾收集。一个实现是   允许推迟垃圾收集或完全省略 - 这是一个   垃圾收集的实施质量问题   实现,只要没有收集任何仍然存在的对象   可到达的。

object.__init__(self[, ...])
     

在创建实例后调用(通过 new ()),   但在它返回给调用者之前。 [...]

object.__del__(self)
     

当实例即将被销毁时调用。 [...]

因此,当__init__被调用时,对象实例已经存在,因为它是由__new__创建的。但对于Python,一般来说,无法保证__del__将被调用。

以下仅适用于CPython,即Python的参考实现。

  

注意(适用于object.__del__(self)

     

del x不直接call x.__del__() - 前者递减了   x的引用计数为1,后者仅在x时被调用   引用计数达到零。 [...]

只要实例的引用计数降为0,就会调用__del__。这与垃圾收集无关。一个小实验:

>>> class A:
...   def __del__(self): print "del"
... 
>>> a = A()
>>> a = None
del
>>> import gc
>>> gc.disable() 
>>> a = A()
>>> a = None
del

如您所见,即使明确禁用了GC,也会调用析构函数。

请注意,这也意味着如果对象层次结构中有循环,则最终会得到永远不会调用__del__的对象,因为Python GC无法处理引用循环。

>>> a1 = A()
>>> a2 = A()
>>> a1.x = a2
>>> a2.x = a1
>>> a1 = None
>>> a2 = None
>>> import gc
>>> gc.collect()
4
>>> gc.garbage
[<__main__.A instance at 0x7f2c66a1d7e8>, <__main__.A instance at 0x7f2c66a1d830>]