关于sys.exit和SystemExit的困惑

3 投票
3 回答
905 浏览
提问于 2025-04-29 19:01

我在这里的几个讨论串和Python文档中读到了关于SystemExit异常的内容。这个讨论不是重复的,因为我在类似的讨论中没有找到答案。

调用sys.exit()和抛出SystemExit这两种方式都会进行清理吗?我知道sys.exit会调用SystemExit,但如果你只是抛出SystemExit异常,是否也会进行清理呢?官方的Python文档对此并没有很清楚。我之所以问这个,是因为我的一个同事认为在代码中写SystemExit更清晰,而且不需要导入sys模块……但我不太确定单纯抛出这个异常是否会进行清理,相比之下,调用sys.exit似乎在调用SystemExit之前会进行一些清理。

暂无标签

3 个回答

0

更新: 让我们详细解释一下这个答案...

我觉得文档里说得很清楚:

退出Python。这是通过引发一个 SystemExit 异常来实现的,因此在 try 语句的 finally 块中指定的清理操作会被执行,并且可以在外层拦截退出尝试。

还有:

因为 exit() 最终“只是”引发一个异常,所以只有在主线程中调用时,它才会退出进程,并且这个异常没有被拦截。

中间缺失的段落对你的问题(大部分情况下)并不重要。现在,这告诉我们 sys.exit 基本上是通过引发 SystemExit 来实现的,带有一个可选的参数,用于关闭虚拟机的代码(指定退出状态码和消息)。 SystemExit仅仅一个普通的异常,这意味着一些事情:

  1. 它可以被捕获。也就是说,你可以写一个 except SystemExit 块,在某个时刻阻止程序退出。
  2. 它会触发当时栈中所有的 finally 块。你可以利用这些块来进行适当的清理。这也包括你可能不知道的 try: finally: 块,因为上下文处理器可能在它们的 __exit__ 方法中有这些块。
  3. 正如第二段提醒你的,只有程序中的主线程以特殊方式处理 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确实会进行某些清理:调用注册的清理函数,以受控的方式销毁对象(这会刷新缓冲区等)。

1

调用 sys.exit() 实际上就是抛出一个 SystemExit 异常。在返回的过程中,异常处理会进行一些清理工作,所以这两种方法的清理效果是一样的。不过,还是建议你用 sys.exit 而不是 SystemExit,因为省去一次导入 sys 没什么意义,它并不会消耗什么资源。调用 sys.exit 是结束 Python 程序的常规方式。

话说回来,你确定在代码中要明确退出程序吗?这样会让你的函数很难被重复使用。很多时候,人们在程序的主函数中使用 sys.exit,其实只要简单地从函数返回就可以了。

1

根据Python 2的文档,关于atexit模块,使用sys.exitSystemExit这两种方式都会正常进行清理工作。而使用os._exit就不会进行这些清理了。

撰写回答