关于sys.exit和SystemExit的困惑
我在这里的几个讨论串和Python文档中读到了关于SystemExit异常的内容。这个讨论不是重复的,因为我在类似的讨论中没有找到答案。
调用sys.exit()和抛出SystemExit这两种方式都会进行清理吗?我知道sys.exit会调用SystemExit,但如果你只是抛出SystemExit异常,是否也会进行清理呢?官方的Python文档对此并没有很清楚。我之所以问这个,是因为我的一个同事认为在代码中写SystemExit更清晰,而且不需要导入sys模块……但我不太确定单纯抛出这个异常是否会进行清理,相比之下,调用sys.exit似乎在调用SystemExit之前会进行一些清理。
3 个回答
更新: 让我们详细解释一下这个答案...
我觉得文档里说得很清楚:
退出Python。这是通过引发一个
SystemExit
异常来实现的,因此在try
语句的finally
块中指定的清理操作会被执行,并且可以在外层拦截退出尝试。
还有:
因为
exit()
最终“只是”引发一个异常,所以只有在主线程中调用时,它才会退出进程,并且这个异常没有被拦截。
中间缺失的段落对你的问题(大部分情况下)并不重要。现在,这告诉我们 sys.exit
基本上是通过引发 SystemExit
来实现的,带有一个可选的参数,用于关闭虚拟机的代码(指定退出状态码和消息)。 SystemExit
是仅仅一个普通的异常,这意味着一些事情:
- 它可以被捕获。也就是说,你可以写一个
except SystemExit
块,在某个时刻阻止程序退出。 - 它会触发当时栈中所有的
finally
块。你可以利用这些块来进行适当的清理。这也包括你可能不知道的try: finally:
块,因为上下文处理器可能在它们的__exit__
方法中有这些块。 - 正如第二段提醒你的,只有程序中的主线程以特殊方式处理
SystemExit
。而且异常不会从一个线程传播到另一个线程,所以只有在主线程中触发时,系统才会退出。
总结一下,如果我们大大简化,Python的主线程看起来像这样:
try:
run_the_main_script()
except SystemExit as e:
invoke the registered "atexit" functions (in a LIFO way)
check e, set exit status, and quit the program
注意,这并不会为你做任何特殊的清理。不过,Python确实会进行某些清理:调用注册的清理函数,以受控的方式销毁对象(这会刷新缓冲区等)。
调用 sys.exit()
实际上就是抛出一个 SystemExit
异常。在返回的过程中,异常处理会进行一些清理工作,所以这两种方法的清理效果是一样的。不过,还是建议你用 sys.exit
而不是 SystemExit
,因为省去一次导入 sys
没什么意义,它并不会消耗什么资源。调用 sys.exit
是结束 Python 程序的常规方式。
话说回来,你确定在代码中要明确退出程序吗?这样会让你的函数很难被重复使用。很多时候,人们在程序的主函数中使用 sys.exit
,其实只要简单地从函数返回就可以了。
根据Python 2的文档,关于atexit模块,使用sys.exit
和SystemExit
这两种方式都会正常进行清理工作。而使用os._exit
就不会进行这些清理了。