关于性能:Python:使用sys.exit或SystemExit的差异和建议

Python: using sys.exit or SystemExit differences and suggestions

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

  • 有什么区别?
  • 什么时候需要在函数内部使用SystemExit或Sys.Exit?
  • 例子

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

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


    sys.exit(s)只是raise SystemExit(s)的简写,如前者的docstring中所述;尝试help(sys.exit)。因此,您可以执行以下操作,而不是示例程序中的任何一个

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

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


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

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

    在sysmodule.c中定义了sys.exit,只运行PyErr_SetObject(PyExc_SystemExit, exit_code);,实际上与直接提升SystemExit相同。具体来说,提升SystemExit可能更快,因为sys.exit需要LOAD_ATTRCALL_FUNCTIONRAISE_VARARGS操作。另外,raise SystemExit生成的字节码稍小(4字节小),(如果使用from sys import exit则会多出1字节,因为sys.exit预计不会返回任何值,因此包括一个额外的POP_TOP

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

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


    我个人的偏好是至少提出SystemExit,然后尽可能接近"main"功能,这样就有最后机会认为它是有效的退出。从设计的角度来看,拥有sys.exit的库/深度嵌入函数非常糟糕。(一般情况下,退出应"尽可能高")。


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

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


    根据文件,sys.exit(s)有效地完成了raise SystemExit(s)的工作,所以基本上是一样的。


    虽然不同之处得到了许多答案,但https://mail.python.org/pipermail/python-list/2016-april/707470.html提出了一个有趣的观点:

    tl;dr:最好只是提出一个"正常"的例外,并且只在脚本的顶层使用SystemExitsys.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==

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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==

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def main():    
        try:
           create_logdir()
           create_dataset()
           unittest.main()    
        except Exception as e:
           logging.exception(e)
           raise SystemExit

    if __name__ == '__main__':    
        main()

    我个人反对这两种做法。我喜欢的模式是这是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
      def main(argv):
        try:
          ...
        except Exception as e:
          logging.exception(e)
          return 1

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

    注意,main()恢复为一个正常函数返回。

    而且,我们中的大多数人都会避免"例外",只让一个除冒泡外的级别:这样就可以获得堆栈回溯调试。我同意它可以阻止记录异常,并使更糟糕的控制台输出,但我认为这是一个胜利。如果你想的话要记录异常,始终会出现以下情况:

    尝试:…除E例外:日志记录。异常(E)提升

    将异常背诵到日志中,仍然让它冒泡出来正常情况下。

    "例外"模式的问题在于它捕获和兽皮每一个例外,不仅仅是你理解的一组狭隘的特定例外。

    最后,我们不希望提出一个纯粹的异常类。在python 3我认为它实际上是被禁止的,所以它是不可移植的不管怎样。但即使在python中,最好还是提供一个异常实例,而不是类:

    提升系统出口(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'

    我更喜欢冒泡的方法,也许是用日志或警告信息,如:

    logging.exception("创建logdir失败:makedirs(%r):%s"%(Log_dir,e))上升

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

    对于非常小的脚本,sys.stderr.write可以,但通常您的函数可能会迁移到到库中以便重用;请考虑stderr不是始终放置消息;而不是读取日志模块根据需要使用error()或wanr()或exception()。还有更多配置输出无需接线的地方的范围它进入你的内部功能。

  • 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

  • 这就是我自己要做的。

    思考:例外情况是否已"处理",即情况是否已处理因为它是预期的而被处理?如果不是,就让例外冒泡出来,这样用户就知道一些不被理解的东西程序已发生。

    最后,从内部执行SystemExit或sys.exit()通常是不好的。除最外面的main()函数以外的任何函数。我抵制它即使在那里,如果写得好,也可以经常调用主函数从其他有用的地方,这使它成为一个有效的图书馆功能(已重复使用)。这样的函数不应该单方面中止程序。太粗鲁了!相反,让例外冒泡:也许main()的调用方需要它,并且可以处理它。通过中止而不是"提高",你已经剥夺了呼叫者即使你自己也有机会做一些适当的事情(即"main")不知道足够的上下文来处理异常。

    所以我是为了"养活"自己。然后只因为你想记录错误。如果不想记录异常,可以避免完全尝试/排除并使用更简单的代码:让调用者担心关于未处理的异常!