Python 中的 RAII:__del__ 有什么意义?

RAII in Python: What's the point of __del__?

乍一看,Python 的 __del__ 特殊方法似乎提供了与 C++ 中的析构函数相同的优点。但是根据 Python 文档 (https://docs.python.org/3.4/reference/datamodel.html),不能保证 对象的 __del__ 方法会被调用!

It is not guaranteed that __del__() methods are called for objects that still exist when the interpreter exits.

所以也就是说,这个方法是没有用的!不是吗?可能会或可能不会被调用的挂钩函数实际上并没有多大用处,因此 __del__ 没有提供任何关于 RAII 的信息。如果我有一些必要的清理工作,我不需要它 运行 有时 ,哦,当 GC 感觉真的需要它时,我需要它 运行 可靠、确定且 100% 的时间

我知道 Python 提供上下文管理器,这对于该任务更有用,但为什么要保留 __del__ 呢?有什么意义?

因为 __del__ 确实被调用了。只是不清楚什么时候会,因为在 CPython 中如果你有循环引用,refcount 机制无法处理对象回收(因此它通过 __del__ 完成)并且必须将它委托给垃圾收集器.

然后垃圾收集器有一个问题:他不知道以什么顺序打破循环引用,因为这可能会引发其他问题(例如,释放另一个对象的终结所需的内存)收集循环的一部分,触发段错误)。

你强调的一点是因为解释器可能会因为阻止它执行清理的原因而退出(例如,它会出现段错误,或者某些 C 模块不礼貌地调用 exit() )。

用于安全对象终结的 PEP 442 已在 3.4 中完成。我建议你看看。

https://www.python.org/dev/peps/pep-0442/

__del__ 是终结器。它不是析构函数。终结器和析构器是完全不同的动物。

析构函数被可靠地调用,并且只存在于具有确定性内存管理的语言(例如C++)中。 Python 的上下文管理器(with 语句)在某些情况下可以达到类似的效果。这些是可靠的,因为对象的寿命是精确固定的;在 C++ 中,对象在显式 deleted 或退出某个范围时死亡(或者当智能指针删除它们以响应其自身的破坏时)。那就是析构函数 运行.

终结器调用不可靠。终结器的唯一有效用法是 an emergency safety net(注意:本文是从 .NET 的角度编写的,但概念翻译得相当好)。例如,open() 返回的文件对象在结束时自动关闭。但是你仍然应该自己关闭它们(例如使用 with 语句)。这是因为对象是由垃圾收集器动态销毁的,垃圾收集器可能会或可能不会立即 运行,并且对于分代垃圾收集,它可能会或可能不会在任何给定的传递中收集某些对象。由于没有人知道我们将来会发明什么样的优化,因此最安全的假设是您无法知道垃圾收集器何时开始收集您的对象。这意味着您不能依赖终结器。

在 CPython 的特定情况下,由于使用了引用计数(这比垃圾收集更简单、更可预测),您可以获得稍微更强的保证。如果您可以确保您永远不会创建涉及给定对象的引用循环,那么该对象的终结器将在可预测的时间点(当最后一个引用死亡时)被调用。这是 CPython 的参考实现,不是 PyPy、IronPython、Jython 的 not或任何其他实现。