我看到一个类中定义了一个__del__
方法。这个方法用于销毁该类的实例。然而,我找不到使用这个方法的地方。这个方法应该如何使用?像这样:obj1.del()
吗?
如何调用__del__
方法?
__del__
是一个终结器(finalizer)。当一个对象的所有引用被删除后,该对象会被垃圾回收,此时__del__
将被调用。
在简单情况下,这可能会在你执行del x
之后立即发生,或者如果x
是一个局部变量,则在函数结束后发生。特别地,如果没有循环引用,CPython(标准Python实现)将立即进行垃圾回收。*
然而,这是CPython的一个实现细节。Python垃圾回收的唯一必须的属性是它发生在所有引用被删除之后,因此这可能不会立即发生,也可能根本不会发生。
此外,变量可以因为多种原因而存活很长时间,例如传播的异常或模块内部检查可以使变量引用计数大于0。另外,变量可以是引用循环的一部分——开启垃圾回收的CPython会打破大多数但不是全部这样的循环,而且只会定期进行。
由于无法保证__del__()
被执行,因此永远不应该将需要运行的代码放入其中——相反,这些代码应该属于try
语句的finally
子句或with
语句的上下文管理器。但是,__del__
也有合法的使用情况:例如,如果一个对象X
引用了Y
并且还在全局cache
中保留了Y
的引用副本(cache['X -> Y'] = Y
),那么X.__del__
也应该删除缓存条目。
如果你知道析构函数提供了(违反上述准则的)必需清理,你可能想要直接调用它,因为它作为一个方法没有任何特殊之处:x.__del__()
。显然,只有在你知道它可能会被调用两次时才应该这样做。或者,作为最后的手段,你可以使用重新定义该方法:
type(x).__del__ = my_safe_cleanup_method
* 参考资料:
CPython 实现细节: CPython 目前使用引用计数方案,通过(可选的)延迟检测循环引用垃圾来收集大多数成为不可达对象的对象[...] 其他实现方式可能有所不同,而且 CPython 也可能会改变。
__exit__
是什么意思?它是在__del__
之前还是之后运行,还是一起运行? - lony__del__
,这样就不必担心第二次调用的问题。 - SarcasticSully__del__
方法可能也不会运行。即使它们在终止时运行,编写一个能够正常工作的__del__
方法,即使解释器正在自毁,也需要比许多程序员应用更谨慎的编码。 (CPython清理通常会在解释器关闭时运行__del__
方法,但仍有一些情况不足够。守护线程、C级全局变量和在另一个__del__
中创建的具有__del__
的对象都可能导致__del__
方法无法运行。) - user2357112__init__
不是构造函数的原因相同。两者都不会实际创建或销毁对象。__init__
方法通过调用它时已经存在的对象来初始化该对象(这也是为什么它不返回任何内容的原因)。同样,当__del__
退出时,对象仍然存在:它只是在实际销毁之前执行任何清理(“最终化”)可能需要完成的工作。 - Peter Hansen我原本为另一个问题写了答案,不过这个问题更加准确。
以下是有点主观的答案。
不要使用__del__
。这不是C++或者专门为析构函数设计的语言。在Python 3.x中,__del__
方法应该被废弃,尽管我确定会有人找到有意义的用例。如果你必须使用__del__
,请注意一下基本限制(详见http://docs.python.org/reference/datamodel.html):
__del__
方法只有在垃圾收集器正在回收对象时才会被调用,并不是在你失去了对一个对象的最后引用或者执行del object
时调用的。__del__
方法负责调用超类中任何__del__
方法,尽管不清楚这是按照方法解析顺序还是直接调用每个超类。__del__
方法意味着垃圾收集器放弃检测和清理任何循环链接,例如失去对链表的最后引用。可以从gc.garbage中获取被忽略的对象列表。有时可以使用弱引用来完全避免这种情况。这个问题经常被讨论:请参见http://mail.python.org/pipermail/python-ideas/2009-October/006194.html。__del__
函数可以作弊,保存一个对象的引用,并停止垃圾收集。__del__
中显式抛出的异常会被忽略。__del__
比 __init__
更多地补充了__new__
。这容易引起混淆。有关说明和注意事项,请参见http://www.algorithm.co.il/blogs/programming/python-gotchas-1-del-is-not-the-opposite-of-init/。__del__
在Python中不受欢迎。您会发现sys.exit()文档未指定退出之前是否收集垃圾,并且存在许多奇怪的问题。调用全局变量上的__del__
会导致奇怪的排序问题,例如http://bugs.python.org/issue5099。如果__init__
失败,是否应该调用__del__
?请参见http://mail.python.org/pipermail/python-dev/2000-March/thread.html#2423进行长时间讨论。但是,另一方面:
__del__
意味着您不会忘记调用关闭语句。请参见http://eli.thegreenplace.net/2009/06/12/safely-using-destructors-in-python/以了解一个支持__del__
的优点。这通常涉及释放ctypes或其他特殊资源。而我个人不喜欢使用__del__
的原因是:
__del__
,它都会退化为三十条令人困惑的消息。因此,请找到一个不使用__del__
的理由。
__del__
,而是如何调用 __del__
,您的答案也很有趣。 - nbro__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
>>>
__del__
时有不同的规则。然而,由于此原因以及在调用时对象及其环境可能处于未知状态,使用__del__
并不被认为是良好的实践。同时也不能保证一定会调用__del__
- 解释器可能以多种方式退出而不删除所有对象。请保留HTML标签。__del__
功能有些不可靠。如果在某些情况下似乎有用,请考虑改用 __enter__
和 __exit__
方法。这将提供类似于用于访问文件的 with open() as f: pass
语法的行为。当进入 with
的作用域时,会自动调用 __enter__
,而当退出时,则会自动调用 __exit__
。有关更多详细信息,请参见此问题。
__del__
的工作原理,并提供了一些替代方案。 - InSync