什么是 __del__ 方法以及如何调用它?
- 2024-12-05 08:37:00
- admin 原创
- 131
问题描述:
我看到一个类中定义了一个__del__
方法。此方法用于销毁类的一个实例。但是,我找不到使用这个方法的地方。这个方法如何使用?就像这样:obj1.del()
?。
我该如何调用该__del__
方法?
解决方案 1:
__del__
是一个finalizer 。当一个对象被垃圾回收时,它会被调用,而垃圾回收发生在对该对象的所有引用都被删除之后的某个时刻。
在简单情况下,这可能发生在您说完之后del x
,或者,如果x
是局部变量,则发生在函数结束后。特别是,除非存在循环引用,否则 CPython(标准 Python 实现)将立即进行垃圾收集。*
但是,这是CPython 的实现细节。Python 垃圾回收的唯一必需属性是它发生在所有引用都被删除之后,因此这可能不一定在之后立即发生,也可能根本不会发生。
更有甚者,变量可以因多种原因而存在很长时间,例如传播异常或模块自省可以使变量引用计数大于 0。此外,变量可以是引用循环的一部分- 启用垃圾收集的 CPython 会中断大多数(但不是全部)这样的循环,而且即使中断也只是定期中断。
由于无法保证它会被执行,因此永远不要将需要运行的代码放入其中__del__()
— 相反,此代码属于语句finally
的子句try
或语句中的上下文管理器with
。但是,有一些有效的用例:__del__
例如,如果一个对象X
引用Y
并且还在Y
全局cache
(cache['X -> Y'] = Y
)中保留引用的副本,那么X.__del__
删除缓存条目也是礼貌的。
如果您知道析构函数提供了(违反上述准则)必需的清理,您可能希望直接调用它,因为它作为一种方法没有什么特别之处:x.__del__()
。显然,只有在您知道它可以被调用两次时才应该这样做。或者,作为最后的手段,您可以使用以下方法重新定义此方法
type(x).__del__ = my_safe_cleanup_method
*参考:
CPython 实现细节: CPython 当前使用引用计数方案,并带有(可选)循环链接垃圾的延迟检测,当大多数对象变得无法访问时,它会立即收集它们 [...] 其他实现的行为有所不同,并且 CPython 可能会发生变化。
解决方案 2:
我写下了另一个问题的答案,但这个问题更为准确。
构造函数和析构函数如何工作?
这是一个略带主观性的回答。
不要使用__del__
。这不是 C++ 或为析构函数构建的语言。该__del__
方法确实应该在 Python 3.x 中消失,尽管我相信有人会找到一个有意义的用例。如果您需要使用__del__
,请注意http://docs.python.org/reference/datamodel.html中的基本限制:
__del__
在垃圾收集器恰好收集对象时被调用,而不是在丢失对对象的最后一个引用时或执行时被调用del object
。__del__
负责调用__del__
超类中的任何内容,但不清楚这是按方法解析顺序(MRO)还是仅仅调用每个超类。拥有
__del__
意味着垃圾收集器放弃检测和清理任何循环链接,例如丢失对链接列表的最后一个引用。您可以从 gc.garbage 获取忽略的对象列表。有时您可以使用弱引用来完全避免循环。这时常引起争论:请参阅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 /了解解释和陷阱。__del__
在 Python 中, sys.exit() 并不是一个“深受喜爱”的孩子。您会注意到, sys.exit() 文档并未指定在退出前是否收集垃圾,并且存在许多奇怪的问题。__del__
在全局变量上调用 会导致奇怪的排序问题,例如http://bugs.python.org/issue5099。__del__
即使__init__
失败也应该调用吗?请参阅http://mail.python.org/pipermail/python-dev/2000-March/thread.html#2423以获取长线程。
但另一方面:
__del__
意味着您不会忘记调用 close 语句。请参阅http://eli.thegreenplace.net/2009/06/12/safely-using-destructors-in-python/了解专业__del__
观点。这通常是关于释放 ctypes 或其他特殊资源。
以及我个人不喜欢该功能的原因__del__
。
每次有人提起
__del__
它,它就会演变成三十条令人困惑的信息。它打破了 Python 之禅的这些原则:
+ 简单比复杂好。
+ 特殊情况还不足以特殊到打破规则。
+ 错误不应该悄无声息地发生。
+ 面对模糊性,拒绝猜测的诱惑。
+ 应该有一种(最好只有一种)明显的方法来做到这一点。
+ 如果实施起来很难解释,那就是一个坏主意。
因此,找一个不使用的理由__del__
。
解决方案 3:
该__del__
方法将在对象被垃圾回收时被调用。请注意,它不一定保证会被调用。以下代码本身不一定会这样做:
del obj
原因是del
引用计数仅减少一。如果其他对象引用了该对象,__del__
则不会被调用。
不过,使用时有一些注意事项__del__
。一般来说,它们通常不是很有用。在我看来,你更想使用 close 方法或with 语句。
请参阅有关方法的Python 文档__del__
。
还有一点需要注意: __del__
如果过度使用方法,可能会抑制垃圾收集。特别是,具有多个带有方法的对象的循环引用不会被垃圾收集。这是因为垃圾收集器不知道先调用哪一个。有关更多信息,请参阅gc 模块__del__
的文档。
解决方案 4:
该__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__
一定会被调用 - 解释器可以以各种方式退出而不删除所有对象。
解决方案 5:
如前所述,该__del__
功能有些不可靠。在可能有用的情况下,请考虑使用__enter__
和__exit__
方法。这将提供类似于用于访问文件的语法的行为with open() as f: pass
。__enter__
在进入范围时会自动调用with
,而在退出时会自动调用。有关更多详细信息,__exit__
请参阅此问题。