77

在线阅读一些程序员使用sys.exit,其他人使用SystemExit
对不起,基本问题:

  1. 有什么区别?
  2. 我什么时候需要在函数中使用 SystemExit 或 sys.exit?

例子

ref = osgeo.ogr.Open(reference)
if ref is None:
    raise SystemExit('Unable to open %s' % reference)

或者

ref = osgeo.ogr.Open(reference)
if ref is None:
    print('Unable to open %s' % reference)
    sys.exit(-1)
4

7 回答 7

41

没有实际区别,但您的示例代码还有另一个区别 -print进入标准输出,但异常文本进入标准错误(这可能是您想要的)。

于 2012-12-21T15:26:07.317 回答
36

sys.exit(s) is just shorthand for raise SystemExit(s), as described in the former's docstring; try help(sys.exit). So, instead of either one of your example programs, you can do

sys.exit('Unable to open %s' % reference)
于 2012-12-21T15:23:09.293 回答
24

除了 raise 之外,还有 3 个退出函数SystemExit

底层是os._exit,它需要 1 个 int 参数,并立即退出而不进行清理。您不太可能会想要触摸这个,但它就在那里。

sys.exit在 sysmodule.c 中定义并运行PyErr_SetObject(PyExc_SystemExit, exit_code);,这实际上与直接提升相同SystemExit。详细地说,加注SystemExit可能更快,因为sys.exit需要一个LOAD_ATTRand CALL_FUNCTIONvsRAISE_VARARGS操作调用。此外,raise SystemExit产生稍小的字节码(少 4 个字节),(如果使用,则额外 1 个字节, from sys import exit因为sys.exit预计返回 None,因此包括一个额外的POP_TOP)。

最后一个退出函数在 REPL中定义site.py,并别名为exitor 。quit它实际上是该类的一个实例Quitter(因此它可以有一个 custom __repr__,因此可能是运行最慢的。此外,它在 raisesys.stdin之前关闭SystemExit,因此建议仅在 REPL 中使用。

至于如何SystemExit处理,最终会导致VM调用os._exit,但在此之前,它会进行一些清理。它还运行atexit._run_exitfuncs()通过atexit模块注册的任何回调。直接调用os._exit会绕过该atexit步骤。

于 2016-02-16T10:49:29.200 回答
12

我个人的偏好是至少SystemExit被提出(甚至更好 - 一个更有意义和有据可查的自定义异常),然后尽可能接近“主要”功能,然后有最后机会将其视为有效退出与否。从设计的角度来看,具有的库/深度嵌入功能sys.exit简直令人讨厌。(一般来说,退出应该“尽可能高”)

于 2012-12-21T15:25:55.100 回答
5

根据文档sys.exit(s)有效地做raise SystemExit(s),所以它几乎是一回事。

于 2012-12-21T15:23:24.050 回答
4

SystemExit是一个例外,这基本上意味着您的程序有一种行为,您想要停止它并引发错误。sys.exit是您可以调用以退出程序的函数,可能会向系统提供返回码。

编辑:它们确实是一回事,所以唯一的区别在于程序背后的逻辑。异常是某种“不需要的”行为,无论从程序员的角度来看,对函数的调用是否更像是“标准”操作。

于 2012-12-21T15:23:46.237 回答
3

虽然许多答案已经回答了差异,但https://mail.python.org/pipermail/python-list/2016-April/707470.html提出了一个有趣的观点:

TL;DR:最好只引发“正常”异常,并仅在脚本的顶层使用SystemExit或使用。sys.exit

我在 python 2.7 和 Linux 上,如果我可以用 raise SystemExit 替换 sys.exit(1) ,我有一个简单的代码需要建议。

==实际代码==

def main():    
    try:
       create_logdir()
       create_dataset()
       unittest.main()    
     except Exception as e:
       logging.exception(e)
       sys.exit(EXIT_STATUS_ERROR)

if __name__ == '__main__':    main()

==更改代码==

def main():    
    try:
       create_logdir()
       create_dataset()
       unittest.main()    
    except Exception as e:
       logging.exception(e)
       raise SystemExit

if __name__ == '__main__':    
    main()

我个人反对这两个。我喜欢的模式是这样的:

  def main(argv):
    try:
      ...
    except Exception as e:
      logging.exception(e)
      return 1

  if __name__ == '__main__':
    sys.exit(main(sys.argv))

请注意, main() 恢复为具有正常返回的正常函数。

此外,我们大多数人会避免“异常除外”,而只是让顶层除外冒泡:这样您就可以获得堆栈回溯以进行调试。我同意它可以防止记录异常并导致更丑的控制台输出,但我认为这是一个胜利。如果您确实想记录异常,则总是这样:

尝试:...除了异常为 e:logging.exception(e) raise

将异常背诵到日志中,仍然让它正常冒泡。

“except Exception”模式的问题在于,它捕获并 隐藏了 每个异常,而不仅仅是您理解的特定异常的狭窄集合。

最后,不赞成提出一个裸露的 Exception 类。在 python 3 中,我相信它实际上是被禁止的,所以无论如何它都是不可移植的。但即使在 Python 中,最好提供一个 Exception 实例,而不是类:

引发 SystemExit(1)

  1. try 块中的所有函数都使用 raise 冒泡了异常

    这里的 create_logdir() 示例是函数定义

定义创建日志目录():

try: os.makedirs(LOG_DIR) 除了 OSError as e: sys.stderr.write("Failed to create log directory...Exiting !!!") raise print "log file:" + corrupt_log return True

def main(): try: create_logdir() except Exception as e: logging.exception(e) raise SystemExit

(a) 如果 create_logdir() 失败,我们将收到以下错误,这很好还是我需要改进此代码。

无法创建日志目录...正在退出 !!!ERROR:root:[Errno 17] 文件存在:'/var/log/dummy'

回溯(最后一次调用):文件“corrupt_test.py”,第 245 行,在 main create_logdir() 文件“corrupt_test.py”,第 53 行,在 create_logdir os.makedirs(LOG_DIR) 文件“/usr/local/lib/ python2.7/os.py",第 157 行,在 makedirs OSError: [Errno 17] 文件存在:'/var/log/dummy'

我更喜欢冒泡方法,可能像您所做的那样带有日志或警告消息,例如:

logging.exception("create_logdir failed: makedirs(%r): %s" % (LOG_DIR, e)) raise

(也不是该日志消息记录了更多上下文:上下文在调试问题时非常有用。)

对于非常小的脚本 sys.stderr.write 是可以的,但一般来说,你的任何被证明通常有用的函数都可能会迁移到库中以便被重用;考虑到 stderr 并不总是消息的地方;而是根据需要使用 error() 或 wanr() 或 exception() 读取日志记录模块。无需将输出连接到您的内部功能,配置输出的位置就可以有更多的空间。

  1. 我可以只 raise ,而不是 SystemExit 或 sys.exit(1) 。这对我来说看起来不对

    定义主():

    尝试:create_logdir() 例外为 e logging.exception(e) raise

这就是我自己会做的。

想一想:异常是否被“处理”了,意味着情况是否因为预期而得到处理?如果没有,让异常冒泡,以便用户知道发生了程序无法理解的事情。

最后,SystemExit 或 sys.exit() 从除了最外层的 main() 函数之外的任何东西中通常都是不好的。我什至在那里抗拒它;主函数,如果写得好,通常可以从其他有用的地方调用,这使得它有效地成为一个库函数(它已被重用)。这样的功能不应该单方面中止程序。真没礼貌!相反,让异常冒出来:也许 main() 的调用者期望它并且可以处理它。通过中止而不是“引发”,您剥夺了调用者做适当事情的机会,即使您自己(即“主要”)不知道足够的上下文来处理异常。

所以我是为了“提高”自己。然后只是因为您想记录错误。如果您不想记录异常,则可以完全避免 try/except 并使用更简单的代码:让调用者担心未处理的异常!

于 2018-03-01T12:32:45.733 回答