【问题标题】:destructor called when failing to create the instance?无法创建实例时调用析构函数?
【发布时间】:2016-08-27 06:09:01
【问题描述】:

在尝试理解一些 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 是实例吗?确实如此,那么在调用构造函数的时候实例已经存在了,是吗?

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

【问题讨论】:

  • 我可能是错的,但我通常认为__del__ 类似于finally 子句,即“无论如何”都会执行的东西。
  • @Kris 有点像。但是,不能保证 __del__ 在解释器退出时仍会被调用。

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


【解决方案1】:

来自 Python 文档:

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

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

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

object.__del__(self)

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

所以在调用__init__ 时对象实例已经存在,因为它是由__new__ 创建的。但是对于一般的 Python,不能保证__del__ 会被调用。

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

注意(object.__del__(self)

del x 不直接call x.__del__() — 前者减少 x 的引用计数加一,后者仅在 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>]

【讨论】:

  • “只要实例的引用计数降至 0,就会调用 __del__。” ...垃圾收集器会在它上面运行。 何时发生这种情况取决于 Python 解释器使用的算法,该算法因实现而异。在极端情况下,甚至可能在整个程序退出之前都不会发生。
  • @Markus 我觉得你的回答很有趣,但我只需要解释一下我不完全理解的最后一句话:“每当实例的引用计数降至 0 时。”。我可能会说一些愚蠢的话,但这是否意味着“当实例不再分配给变量时。”
  • @vmonteco 看看docs.python.org/2/reference/datamodel.html#object.__del__。这能回答你的问题吗?
  • @Markus 我切换到了 python3 doc 版本,但我认为可以,谢谢! :)
  • @vmonteco 我添加了一个小例子,说明何时永远不会调用 __del__
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-11
  • 2011-08-30
  • 2010-10-16
  • 1970-01-01
相关资源
最近更新 更多