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

时间:2009-09-26 15:49:52

标签: python oop

我正在读代码。有一个类定义了__del__方法。我发现这个方法用于销毁类的实例。但是,我找不到使用此方法的地方。主要原因是我不知道如何使用这种方法,可能不是这样:obj1.del()。那么,我的问题是如何调用__del__方法?谢谢你的帮助。

5 个答案:

答案 0 :(得分:118)

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

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

但是,这是CPython的实现细节。 Python垃圾收集的唯一必需属性是所有引用都被删除后发生,因此可能不必在之后发生和<强>可能根本不会发生。

更重要的是,变量可以长时间存在<很多原因,例如传播异常或模块内省可以使变量引用计数大于0.此外,变量可以是引用循环的一部分 - 启用垃圾收集的CPython会破坏大多数但不是全部,这样的循环,甚至只是定期。

由于您无法保证它已被执行,因此永远不会将您需要运行的代码放入__del__() - 相反,此代码属于finally条款try块或with语句中的上下文管理器。但是,__del__有效用例:例如:如果对象X引用了Y并且还在全局Ycache)中保留了cache['X -> Y'] = Y引用的副本,那么{{1}将是礼貌的还要删除缓存条目。

如果您知道析构函数提供(违反上述指南)必要的清理工作,您可能需要直接调用,因为没有什么特别之处它作为一种方法:X.__del__。显然,只有当你知道它不介意被召唤两次时你才应该这样做。或者,作为最后的手段,您可以使用

重新定义此方法
x.__del__()

答案 1 :(得分:54)

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

How do constructors and destructors work?

这是一个有点自以为是的答案。

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

    当垃圾收集器碰巧收集对象时,
  • __del__被调用,而不是在您丢失对象的最后一个引用时,而不是在您执行__del__时。
  • del object负责调用超类中的任何__del__,但不清楚这是在方法解析顺序(MRO)中还是仅调用每个超类。
  • 拥有__del__意味着垃圾收集器放弃检测和清除任何循环链接,例如丢失对链表的最后一个引用。您可以从gc.garbage获取忽略的对象列表。有时您可以使用弱引用来完全避免循环。这有时会引起争议:见http://mail.python.org/pipermail/python-ideas/2009-October/006194.html
  • __del__函数可以作弊,保存对象的引用,并停止垃圾回收。
  • 忽略__del__中明确提出的例外情况。
  • __del__补充__del__远远超过__new__。这让人感到困惑。有关解释和问题,请参阅http://www.algorithm.co.il/blogs/programming/python-gotchas-1-del-is-not-the-opposite-of-init/
  • __init__不是Python中“深受喜爱”的孩子。您会注意到sys.exit()文档没有指定在退出之前是否收集垃圾,并且存在许多奇怪的问题。在全局变量上调用__del__会导致奇怪的排序问题,例如http://bugs.python.org/issue5099。即使__del__失败,__del__是否应该调用?有关长线程,请参阅http://mail.python.org/pipermail/python-dev/2000-March/thread.html#2423

但是,另一方面:

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

  • 每当有人提起__del__时,它就会变成三十条混乱信息。
  • 它打破了Python的禅宗中的这些项目:
    • 简单比复杂更好。
    • 特殊情况不足以打破规则。
    • 错误永远不会无声地传递。
    • 面对歧义,拒绝猜测的诱惑。
    • 应该有一个 - 最好只有一个 - 显而易见的方法。
    • 如果实施难以解释,那就不错了。

因此,找个理由不使用__del__

答案 2 :(得分:11)

__del__方法,当对象被垃圾收集时将调用它。请注意,它不一定能保证被调用。以下代码本身不一定会这样做:

del obj

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

但使用__del__时有一些注意事项。通常,它们通常不是很有用。听起来我更像是想要使用close方法或with statement

请参阅python documentation on __del__ methods

另外需要注意的一点是:__del__方法可以在过度使用时禁止垃圾回收。特别是,具有多个具有__del__方法的对象的循环引用不会被垃圾收集。这是因为垃圾收集器不知道首先调用哪一个。有关详细信息,请参阅gc module上的文档。

答案 3 :(得分:8)

当您的对象最终被销毁时,将调用__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__ - 解释器可以以各种方式退出而不删除所有对象。

答案 4 :(得分:0)

如前所述,__del__功能有些不可靠。在看起来有用的情况下,请考虑使用__enter____exit__方法。这将产生类似于用于访问文件的with open() as f: pass语法的行为。进入__enter__的范围时,会自动调用with,而退出时会自动调用__exit__。有关更多详细信息,请参见this question