什么是 __del__ 方法以及如何调用它?

我看到一个定义了 __del__的类。此方法用于销毁类的实例。但是,我找不到使用此方法的位置。如何使用这种方法?像这样: obj1.del()?.

如何调用 __del__方法?

172933 次浏览

__del__终结者。当一个对象是 垃圾收集时调用它,该对象在删除对该对象的所有引用之后的某个时刻发生。

简单的案子中,这可能是在你说 del x之后,或者,如果 x是一个局部变量,在函数结束之后。特别是,除非有循环引用,否则 CPython (标准 Python 实现)将立即进行垃圾收集。

但是,这是 CPython 的 实施细节。Python 垃圾收集的唯一 需要属性是发生 之后所有引用都已被删除,因此这可能不一定发生 就在那之后可能根本不会发生

更重要的是,对于 很多原因,变量可以存活很长时间,例如,传播异常或模块自省可以使变量引用计数保持大于0。此外,变量可以是 参考文献循环ー CPython 的一部分,其中大部分(但不是全部)这样的周期打开了垃圾收集,甚至只是周期性地打开。

因为不能保证执行,所以应该由 finally1将需要运行的代码放入 __del__()ーー而不是,这段代码属于 try块的 finally子句或 with语句中的上下文管理器。然而,对于 __del__finally2: 例如,如果一个对象 X引用 Y,并且在全局 cache(cache['X -> Y'] = Y)中保留 Y引用的一个副本,那么对于 finally0也删除缓存条目是有礼貌的。

如果您知道 析构函数提供了(违反上述准则)所需的清理,那么您可能需要 直接打电话,因为它作为方法没有什么特殊之处: x.__del__()。显然,只有当您知道它可以被调用两次时,才应该这样做。或者,作为最后的手段,您可以使用

type(x).__del__ = my_safe_cleanup_method

__del__方法(注意拼写!)当对象最终被销毁时调用。从技术上讲(在 cPython 中) ,这是指没有更多的对象引用,即当它超出作用域时。

如果要删除对象并因此调用 __del__方法,请使用

del obj1

它将删除该对象(假设没有任何其他对它的引用)。

我建议您编写一个这样的小类

class T:
def __del__(self):
print "deleted"

并在 Python 解释器中进行调查,例如

>>> a = T()
>>> del a
deleted
>>> a = T()
>>> b = a
>>> del b
>>> del a
deleted
>>> def fn():
...     a = T()
...     print "exiting fn"
...
>>> fn()
exiting fn
deleted
>>>

注意,jython 和 ironpython 对于删除对象和调用 __del__的确切时间有不同的规则。尽管使用 __del__被认为是不好的实践,但是由于这个原因,以及对象及其环境在被调用时可能处于未知状态的事实。也不能绝对保证 __del__会被调用——解释器可以通过各种方式退出,而无需删除所有对象。

当对象被垃圾收集时,将调用 __del__方法。但是请注意,并不一定要保证调用它。下面的代码本身不一定能做到这一点:

del obj

原因是 del只是将引用计数减1。如果其他对象有对该对象的引用,则不会调用 __del__

不过,使用 __del__需要注意一些问题。一般来说,他们只是不太有用。在我看来,这更像是你想要使用一个 close 方法或者 发表声明

看看 关于 __del__方法的 python 文档

还有一点需要注意: 如果过度使用,__del__方法可以抑制垃圾收集。特别是,具有多个具有 __del__方法的对象的循环引用不会被垃圾收集。这是因为垃圾收集器不知道先调用哪一个。有关更多信息,请参见 气相色谱模块上的文档。

我写了另一个问题的答案,尽管这个问题更准确。

构造函数和析构函数是如何工作的?

这是一个有点固执己见的答案。

不要用 __del__。这不是 C + + ,也不是为析构函数构建的语言。在 Python 3.x 中,__del__方法确实应该消失,尽管我确信有人会找到一个有意义的用例。如果您需要使用 __del__,请注意每个 http://docs.python.org/reference/datamodel.html的基本限制:

  • 当垃圾收集器正好在收集对象时调用 __del__,而不是在丢失对对象的最后一个引用时调用,也不是在执行 del object时调用。
  • __del__负责调用超类中的任何 __del__,尽管不清楚这是按照方法解析顺序(MRO)还是仅仅调用每个超类。
  • 拥有 __del__意味着垃圾收集器放弃检测和清理任何循环链接,例如丢失对链表的最后一个引用。您可以获取从 gc.环境中忽略的对象的列表。有时可以使用弱引用来完全避免循环。这个问题时不时会引起争论: 请参阅 http://mail.python.org/pipermail/python-ideas/2009-October/006194.html
  • __del__函数可以作弊,保存对对象的引用,并停止垃圾回收。
  • 忽略在 __del__中显式引发的异常。
  • __del____new__的互补性远远大于 __init__。这会让人感到困惑。请参阅 http://www.algorithm.co.il/blogs/programming/python-gotchas-1-del-is-not-the-opposite-of-init/以获得解释和陷阱。
  • 在 Python 中,__del__不是一个“受欢迎的”孩子。您将注意到,sys.exit ()文档没有指定是否在退出之前收集了垃圾,而且存在许多奇怪的问题。在全局上调用 __del__会导致奇怪的排序问题,例如 http://bugs.python.org/issue5099。即使 __init__失败,__del__也应该调用吗?有关长线程,请参见 http://mail.python.org/pipermail/python-dev/2000-March/thread.html#2423

但另一方面:

以及我个人不喜欢 __del__功能的原因。

  • 每次有人提起 __del__,它就会转变成30条令人困惑的信息。
  • 它打破了 Python 禅宗中的这些条目:
    • 简单总比复杂好。
    • 特殊情况不足以破坏规则。
    • 错误永远不会悄无声息地过去。
    • 面对模棱两可的情况,拒绝猜测的诱惑。
    • 应该有一个,最好只有一个明显的方法来做到这一点。
    • 如果实现很难解释,那就是个坏主意。

因此,找到不使用 __del__的理由。

如前所述,__del__功能有些不可靠。在它看起来有用的情况下,考虑使用 __enter____exit__方法代替。这将提供类似于用于访问文件的 with open() as f: pass语法的行为。当进入 with的作用域时自动调用 __enter__,而当退出作用域时自动调用 __exit__。有关详细信息,请参阅 这个问题