正确的做法是在某个时候明确释放您的强引用,而不是指望关闭来做到这一点。
特别是,如果模块被释放,它的全局变量将被释放......但似乎没有记录任何模块将被释放的地方。因此,在关闭时可能仍然存在对您的对象的引用。而且,正如 Martijn Pieters 指出的那样:
不能保证__del__()
为解释器退出时仍然存在的对象调用方法。
但是,如果您可以确保在解释器退出之前的某个时间没有对您的对象的(非弱)引用,则可以保证您的清理运行。
您可以使用atexit
处理程序在自己之后显式清理,但您可以在脱离主模块(或调用sys.exit
,或完成最后一个非守护线程,或其他)之前显式地进行清理。最简单的做法通常是获取整个main
函数并将其包装在 a with
or try
/finally
中。
或者,更简单地说,不要尝试将清理代码放入__del__
方法或弱引用回调中;只需将清理代码本身放入您的with
orfinally
或atexit
.
在对另一个答案的评论中:
我实际上想要做的是关闭一个通常由计时器保持打开的子进程,但需要在程序退出时进行核对。是唯一真正“可靠”的方法来启动一个守护子进程来分别监视和杀死另一个进程吗?
做这种事情的通常方法是用可以从外部发出信号的东西来代替计时器。在不知道您的应用程序架构和您正在使用哪种计时器的情况下(例如,反应器启动计时器的单线程异步服务器与操作系统计时器消息启动计时器的单线程异步 GUI 应用程序与多-线程应用程序,其中计时器只是sleep
间隔与...之间的线程),很难更具体地解释。
同时,您可能还想看看是否有更简单的方法来处理您的子流程。例如,可能使用显式进程组,并杀死您的进程组而不是您的进程(这将杀死所有子进程,在 Windows 和 Unix 上……尽管细节非常不同)?或者也许给子进程一个管道并在管道的另一端出现故障时让它退出?
请注意,文档也不保证删除剩余引用的顺序(如果有的话)。事实上,如果您使用的是 CPython,Py_Finalize
特别说明它是“按随机顺序完成的”。
来源很有趣。它显然不是明确随机的,甚至不是完全任意的。首先它进行 GC 收集,直到什么都没有留下,然后它最终确定 GC 本身,然后它执行 a (PyImport_Cleanup
基本上sys.modules.clear()
就是_PyImport_Fini
仅作为“仅供内部使用”)。
但这意味着,假设您的模块确实持有对您的对象的唯一(非弱)引用,并且没有涉及模块本身的牢不可破的循环,您的模块将在关闭时被清理,这将删除最后引用您的对象,导致它也被清理。(当然,除了内建、扩展模块和您现在仍然存在的直接引用的东西之外,您不能指望其他任何东西……但是您上面的代码应该没问题,因为foo
之前无法清理Foo
,而且它没有t 依赖于任何其他非内置函数。)
请记住,这是特定于 CPython 的——实际上是特定于 CPython 3.3 的;您将需要阅读您的版本的相关等效源以确保。同样,文档明确指出,事情会“以随机顺序”被删除,因此如果您不想依赖特定于实现的行为,那么这就是您所期望的。
当然,您的清理代码仍然不能保证被调用。例如,未处理的信号(在 Unix 上)或结构化异常(在 Windows 上)将杀死解释器,而不给它清理任何东西的机会。即使您为此编写处理程序,也总是有人会拉电源线。因此,如果您需要一个完全健壮的设计,您需要在任何时候都无需清理即可中断(通过日志、使用原子文件操作、具有显式确认的协议等)。