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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13992662/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 10:07:08  来源:igfitidea点击:

Python: using sys.exit or SystemExit differences and suggestions

pythonperformancecoding-style

提问by Gianni Spear

Reading online some programmers use sys.exit, others use SystemExit.
Sorry for the basic question:

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

  1. What is the difference?
  2. When do I need to use SystemExit or sys.exit inside a function?
  1. 有什么不同?
  2. 我什么时候需要在函数内使用 SystemExit 或 sys.exit?

Example

例子

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

or

或者

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

采纳答案by RichieHindle

No practical difference, but there's another difference in your example code - printgoes to standard out, but the exception text goes to standard error (which is probably what you want).

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

回答by Fred Foo

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(s)只是 的简写raise SystemExit(s),如前者的文档字符串中所述;试试help(sys.exit)。因此,您可以执行以下任一示例程序,而不是

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

回答by piokuc

According to documentation sys.exit(s)effectively does raise SystemExit(s), so it's pretty much the same thing.

根据文档sys.exit(s)有效地确实raise SystemExit(s)如此,所以它几乎是一样的。

回答by Cynical

SystemExitis an exception, which basically means that your progam had a behavior such that you want to stop it and raise an error. sys.exitis the function that you can call to exit from your program, possibily giving a return code to the system.

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

EDIT: they are indeed the same thing, so the only difference is in the logic behind in your program. An exception is some kind of "unwanted" behaviour, whether a call to a function is, from a programmer point of view, more of a "standard" action.

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

回答by Jon Clements

My personal preference is that at the very least SystemExitis raised (or even better - a more meaningful and well documented custom exception) and then caught as close to the "main" function as possible, which can then have a last chance to deem it a valid exit or not. Libraries/deeply embedded functions that have sys.exitis just plain nasty from a design point of view. (Generally, exiting should be "as high up" as possible)

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

回答by Perkins

There are 3 exit functions, in addition to raising SystemExit.

除了提高SystemExit.

The underlying one is os._exit, which requires 1 int argument, and exits immediately with no cleanup. It's unlikely you'll ever want to touch this one, but it is there.

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

sys.exitis defined in sysmodule.c and just runs PyErr_SetObject(PyExc_SystemExit, exit_code);, which is effectively the same as directly raising SystemExit. In fine detail, raising SystemExitis probably faster, since sys.exitrequires an LOAD_ATTRand CALL_FUNCTIONvs RAISE_VARARGSopcalls. Also, raise SystemExitproduces slightly smaller bytecode (4bytes less), (1 byte extra if you use from sys import exitsince sys.exitis expected to return None, so includes an extra POP_TOP).

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

The last exit function is defined in site.py, and aliased to exitor quitin the REPL. It's actually an instance of the Quitterclass (so it can have a custom __repr__, so is probably the slowest running. Also, it closes sys.stdinprior to raising SystemExit, so it's recommended for use only in the REPL.

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

As for how SystemExitis handled, it eventually causes the VM to call os._exit, but before that, it does some cleanup. It also runs atexit._run_exitfuncs()which runs any callbacks registered via the atexitmodule. Calling os._exitdirectly bypasses the atexitstep.

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

回答by serv-inc

While the difference has been answered by many answers, https://mail.python.org/pipermail/python-list/2016-April/707470.htmlmakes an interesting point:

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

TL;DR: It's better to just raise a "normal" exception, and use SystemExitor sys.exitonly at the top levels of a script.

TL;DR:最好只引发一个“正常”异常,并且只在脚本的顶层使用SystemExitor sys.exit

I m on python 2.7 and Linux , I have a simple code need suggestion if I I could replace sys.exit(1) with raise SystemExit .

==Actual code==

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()

==Changed Code==

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

if __name__ == '__main__':    
    main()

I am against both of these personally. My preferred pattern is like this:

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

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

Notice that main() is back to being a normal function with normal returns.

Also, most of us would avoid the "except Exception" and just let a top level except bubble out: that way you get a stack backtrace for debugging. I agree it prevents logging the exception and makes for uglier console output, but I think it is a win. And if you dowant to log the exception there is always this:

try: ... except Exception as e: logging.exception(e) raise

to recite the exception into the log and still let it bubble out normally.

The problem with the "except Exception" pattern is that it catches and hideseveryexception, not merely the narrow set of specific exceptions that you understand.

Finally, it is frowned upon to raise a bare Exception class. In python 3 I believe it is actually forbidden, so it is nonportable anyway. But even In Python to it is best to supply an Exception instance, not the class:

raise SystemExit(1)

  1. All the functions in try block have exception bubbled out using raise

    Example for create_logdir() here is the function definition

def create_logdir():

try: os.makedirs(LOG_DIR) except 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) In case if create_logdir() fails we will get the below error ,is this fine or do I need to improve this code.

Failed to create log directory...Exiting !!!ERROR:root:[Errno 17] File exists: '/var/log/dummy'

Traceback (most recent call last): File "corrupt_test.py", line 245, in main create_logdir() File "corrupt_test.py", line 53, in create_logdir os.makedirs(LOG_DIR) File "/usr/local/lib/python2.7/os.py", line 157, in makedirs OSError: [Errno 17] File exists: '/var/log/dummy'

I prefer the bubble out approach, perhap with a log or warning messages as you have done, eg:

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

(Also not that that log message records more context: context is very useful when debugging problems.)

For very small scripts sys.stderr.write is ok, but in general any of your functions that turned out to be generally useful might migrate into a library in order to be reused; consider that stderr is not always the place for messages; instead reading for the logging module with error() or wanr() or exception() as appropriate. There is more scope for configuring where the output goes that way without wiring it into your inner functions.

  1. Can I have just raise , instead of SystemExit or sys.exit(1) . This looks wrong to me

    def main():

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

This is what I would do, myself.

Think: has the exception been "handled", meaning has the situation been dealt with because it was expected? If not, let the exception bubble out so that the user knows that something notunderstood by the program has occurred.

Finally, it is generally bad to SystemExit or sys.exit() from inside anything other than the outermost main() function. And I resist it even there; the main function, if written well, may often be called from somewhere else usefully, and that makes it effectively a library function (it has been reused). Such a function should not unilaterally abort the program. How rude! Instead, let the exception bubble out: perhaps the callerof main() expects it and can handle it. By aborting and not "raise"ing, you have deprived the caller of the chance to do something appropriate, even though you yourself (i.e. "main") do not know enough context to handle the exception.

So I am for "raise" myself. And then only because you want to log the error. If you didn't want to log the exception you could avoid the try/except entirelyand have simpler code: let the caller worry about unhandled exceptions!

我在 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() 示例是函数定义

def create_logdir():

尝试: os.makedirs(LOG_DIR) 除了 OSError 作为 e: sys.stderr.write("Failed to create log directory...Exiting !!!") raise print "log file:" +corruption_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 行,在主 create_logdir() 文件“corrupt_test.py”,第 53 行,在 create_logdir os.makedirs(LOG_DIR) 文件“/usr/local/lib/ python2.7/os.py", line 157, in 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() 除了 Exception as e logging.exception(e) 引发

这就是我会做的,我自己。

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

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

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