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

时间:2012-12-21 15:20:16

标签: python performance coding-style

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

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

    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)
    

7 个答案:

答案 0 :(得分:24)

sys.exit(s)只是raise SystemExit(s)的简写,如前者的文档字符串所述;试试help(sys.exit)。因此,您可以执行

而不是任何一个示例程序
sys.exit('Unable to open %s' % reference)

答案 1 :(得分:21)

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

答案 2 :(得分:10)

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

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

sys.exit在sysmodule.c中定义,只运行PyErr_SetObject(PyExc_SystemExit, exit_code);,实际上与直接引发SystemExit相同。详细而言,提升SystemExit可能会更快,因为sys.exit需要LOAD_ATTRCALL_FUNCTIONRAISE_VARARGS操作号。此外,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步骤。

答案 3 :(得分:6)

我个人的偏好是至少提出SystemExit(或者更好 - 一个更有意义且记录良好的自定义异常),然后尽可能接近“主要”功能,然后可以有最后的机会认为它是一个有效的退出。从设计的角度来看,具有sys.exit的库/深度嵌入的函数简直令人讨厌。 (一般来说,退出应该“尽可能高”)

答案 4 :(得分:3)

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

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

答案 5 :(得分:2)

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

答案 6 :(得分: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)中       提高

     

将异常列入日志并仍然让它冒出来   通常

     

除了例外""模式是它捕捉和      每个例外,而不仅仅是您理解的一组特定的例外情况。

     

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

     

提升SystemExit(1)

     
    
        
  1. try块中的所有函数都使用raise

    冒出异常          

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

  2.               

    def create_logdir():

             

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

             

    def main():        尝试:            create_logdir()        除了例外e:            logging.exception(e)中            提升SystemExit

             

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

             

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

             

    追踪(最近一次通话):      文件" corrupt_test.py",第245行,在main中        create_logdir()      在create_logdir中输入文件" corrupt_test.py",第53行        os.makedirs(LOG_DIR)      文件" /usr/local/lib/python2.7/os.py" ;,第157行,在makedirs中     OSError:[Errno 17]文件存在:' / var / log / dummy'

      
     

我更喜欢冒泡的方法,记录日志或警告   你已经做过的消息,例如:

     

logging.exception(" create_logdir失败:makedirs(%r):%s"%   (LOG_DIR,e))提高

     

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

     

对于非常小的脚本,sys.stderr.write是可以的,但通常是任何一个   结果通常有用的功能可能会迁移   进入图书馆以便重复使用;认为stderr不是   永远是消息的地方;而是读取记录模块   适当时使用error()或wanr()或exception()。还有更多   在没有布线的情况下配置输出的方式的范围   它进入你的内在功能。

     
    
        
  1. 我可以加注,而不是SystemExit或sys.exit(1)。这个     看起来不对我

             

    def main():

             

    尝试:        create_logdir()     除了例外的e        logging.exception(e)中        提高

  2.        
     

这就是我自己要做的事。

     

思考:有例外情况"处理",意味着有这种情况   因为预期会被处理?如果没有,请让例外   冒泡,以便用户知道所理解的东西   该计划已经发生。

     

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

     

所以我是为了#34;加注"我。然后只是因为你想记录   错误。如果你不想记录异常,你可以避免   尝试/除了完全并且代码更简单:让来电者担心   关于未处理的例外情况!