Python:使用sys.exit或SystemExit的差异和建议

问题描述 投票:37回答:7

在线阅读一些程序员使用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)
python performance coding-style
7个回答
21
投票

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


24
投票

sys.exit(s)只是raise SystemExit(s)的简写,正如前者的文档中描述的那样;试试help(sys.exit)。因此,您可以这样做,而不是您的任何一个示例程序

sys.exit('Unable to open %s' % reference)

10
投票

除了提升SystemExit外,还有3个退出功能。

底层的是os._exit,它需要1个int参数,并且在没有清理的情况下立即退出。你不太可能想触摸这个,但它就在那里。

sys.exit在sysmodule.c中定义,只运行PyErr_SetObject(PyExc_SystemExit, exit_code);,实际上与直接提升SystemExit相同。在细节上,提高SystemExit可能更快,因为sys.exit需要LOAD_ATTRCALL_FUNCTION vs RAISE_VARARGS opcalls。此外,raise SystemExit产生稍小的字节码(少4字节),(如果使用from sys import exit则额外增加1个字节,因为预期sys.exit将返回None,因此包含额外的POP_TOP)。

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

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


6
投票

我个人的偏好是,至少SystemExit被提升(或者甚至更好 - 一个更有意义且记录良好的自定义异常),然后尽可能接近“主要”功能,这可能有最后一次机会认可它是否有效退出。从设计的角度来看,拥有sys.exit的图书馆/深度嵌入式功能简直令人讨厌。 (一般来说,退出应尽可能“高涨”)


3
投票

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

编辑:它们确实是同一个东西,所以唯一的区别在于你的程序背后的逻辑。一个例外是某种“不需要的”行为,无论从程序员的角度来看,对函数的调用是更多的“标准”动作。


2
投票

根据文件sys.exit(s)有效地做raise SystemExit(s),所以它几乎是相同的事情。


0
投票

尽管许多答案都回答了这种差异,但https://mail.python.org/pipermail/python-list/2016-April/707470.html提出了一个有趣的观点:

TL; DR:最好只引发“正常”异常,并仅在脚本的顶层使用SystemExitsys.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

将异常记录到日志中仍然让它正常冒出来。

“异常除外”模式的问题在于它捕获并隐藏了每个异常,而不仅仅是您理解的一小组特定异常。

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

提升SystemExit(1)

  1. try块中的所有函数都有使用raise冒出的异常 create_logdir()的示例是函数定义

def create_logdir():

尝试:os.makedirs(LOG_DIR),除了OSError为e:sys.stderr.write(“无法创建日志目录...退出!!!”)提示打印“日志文件:”+ corrupt_log返回True

def main():try:create_logdir()除了Exception为e:logging.exception(e)引发SystemExit

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

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

回溯(最近调用最后一次):文件“corrupt_test.py”,第245行,在主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. 我可以加注,而不是SystemExit或sys.exit(1)。这看起来对我不对 def main(): 尝试:create_logdir()除了异常,因为e logging.exception(e)raise

这就是我自己要做的。

想一想:异常是“处理”了,意味着已经处理了这种情况,因为它是预期的吗?如果没有,请让异常冒出来,以便用户知道程序不理解的内容已经发生。

最后,除了最外层的main()函数之外的任何东西,SystemExit或sys.exit()通常都是坏的。我甚至在那里抵抗它;主要功能,如果写得好,通常可以从其他地方有用地调用,这使它有效地成为库函数(它已被重用)。这种功能不应单方面中止该计划。太粗鲁了!相反,让异常冒出来:也许main()的调用者需要它并且可以处理它。通过中止而不是“提升”,即使你自己(即“主要”)不知道足够的上下文来处理异常,你也剥夺了调用者做某事的机会。

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

© www.soinside.com 2019 - 2024. All rights reserved.