Python之异常处理

  • 2019 年 11 月 6 日
  • 笔记

Python之异常处理

1

Python中的错误处理

在程序运行的过程中,难免会出现这样那样的错误,有些错误是我们自己程序编写上有问题,也就是程序员听了会砍人的那句话,"哟,写bug呢!",还有一种是无法预测的错误,例如磁盘写满了,又或者从网络抓取数据的时候,网络连接突然崩溃等等。Python中内置了一套异常处理机制,可以帮助我们对这些错误进行处理。他就是try…except…finally的错误处理机制。

首先我们来看一个应用实例:

try:      print('try...')      r =  /      print('result:', r)  except ZeroDivisionError as e:      print('except:', e)  finally:      print('finally...')  print('END')  

上面的方法中,当我们认为某些代码可能存在一定的安全隐患的时候,可以使用try来运行这段代码,这样做的好处是,如果这段代码真的存在错误,则后续的代码不会执行,而是会直接跳转至错误处理代码,也就是except模块,执行完except之后,如果有finally语句,则执行finally语句,否则执行完毕,上面的代码很明显,除数为0的算法肯定是错误的。我们把它写成一个函数test(),可以看到输出值如下(代码可以左滑):

>>> def test(a):  ...     try:  ...             print('try...')  ...             r =  / a  ...             print('result:', r)  ...     except ZeroDivisionError as e:  ...             print('except:', e)  ...     finally:  ...             print('finally...')  ...     print('END')  ...     return  ...  >>> a=test()  try...  ('result:', )  finally...  END  >>> b=test()  try...  ('except:', ZeroDivisionError('integer division or modulo by zero',))  finally...  END  >>>  

我们输入参数0的时候,函数返回了错误码和相应的提示。

在上面的例子中,只定义了一种错误,实际情况中,可能有各种各样的非法输入,这就需要我们制定不同的except,从而对真正的错误原因进行区分:

>>> def test(a):  ...     try:  ...             print('try...')  ...             r =  / int(a)  ...             print('result:', r)  ...     except ValueError as e:  ...             print('ValueError:', e)  ...     except ZeroDivisionError as e:  ...             print('ZeroDivisionError:', e)  ...     finally:  ...             print('finally...')  ...     print('END')  ...  >>> a=test('a')  try...  ('ValueError:', ValueError("invalid literal for int() with base 10: 'a'",))  finally...  END  >>> b=test()  try...  ('result:', )  finally...  END  >>> c=test()  try...  ('ZeroDivisionError:', ZeroDivisionError('integer division or modulo by zero',))  finally...  END  >>>  

上面的结果可以看出,当我们输入一个字母a的时候,返回的错误结果和输入数字0的结果不同,但是他们都触发了异常捕获。

使用try…except还有另外一个好处,就是可以跨越多层调用,比如函数main()调用bar(),bar()调用foo(),如果foo()函数出错了,此时,只要main函数捕获到了,就可以进行处理:

def foo(s):      return  / int(s)    def bar(s):      return foo(s) *    def main():      try:          foo('0')      except Exception as e:          print('Error:', e)      finally:          print('finally...')  

也就是说,不需要在每个可能出错的地方去捕获错误,只要在合适的层次去捕获错误就可以了。这样一来,就大大减少了写try...except...finally的麻烦。

如果错误没有被捕获,它就会一直往上抛,最后被Python解释器捕获,打印一个错误信息,然后程序退出。来看一个错误的例子:

# err.py:  def foo(s):      return  / int(s)    def bar(s):      return foo(s) *    def main():      bar('0')    main()  

当我们调用这个文件的时候,会出现如下的错误:

$ python err.py  Traceback (most recent call last):    File "err.py", line , in <module>      main()    File "err.py", line , in main      bar('0')    File "err.py", line , in bar      return foo(s) *    File "err.py", line , in foo      return  / int(s)  ZeroDivisionError: division by zero  

层层分析,最后我们可以发现是在foo函数中使用0作为分母,从而出现了错误。这个过程中,我们可以看到函数的调用栈是由外而内的。

2

记录错误,继续执行

当出现错误的时候,如果我们想要继续执行后面的程序,对当前的错误仅仅做一个捕获操作,我们可以使用Python内置的logging模块:

# err_logging.py    import logging    def foo(s):      return  / int(s)    def bar(s):      return foo(s) *    def main():      try:          bar('0')      except Exception as e:          logging.exception(e)    main()  print('继续执行,结果是...')  

当我们对上面这段代码进行执行的时候,我们可以看到如下结果:

ERROR:root:integer division or modulo by zero  Traceback (most recent call last):    File "<stdin>", line , in main    File "<stdin>", line , in bar    File "<stdin>", line , in foo  ZeroDivisionError: integer division or modulo by zero  继续执行,结果是...  

函数最终还是执行了print,但是将中途所有的错误都捕获到了。这就是logging的作用,需要注意的是,在使用logging之前,先要对logging模块进行导入。通过配置,logging还可以把错误记录到日志文件中,方便以后排查。

3

抛出错误

在Python中,每一个错误都是一个class,所有的错误类型都继承自BaseException,在使用except的时候需要注意,它不但不获该类型的错误,还把其子类的错误一网打尽。捕获一个错误就是捕获该class的一个实例,Python内置的函数会抛出很多类型的错误,如果我们想自己自定义一个错误,可以使用下面的方法:

# err_raise.py  class FooError(ValueError):      pass    def foo(s):      n = int(s)      if n==:          raise FooError('invalid value: %s' % s)      return  / n    foo('0')  

执行上面这段代码,我们可以通过跟踪找到我们自己定义的错误类型:

>>> python err_raise.py  Traceback (most recent call last):    File "<stdin>", line , in <module>    File "<stdin>", line , in foo  __main__.FooError: invalid value:  >>>  

只有在必要的时候才定义我们自己的错误类型。如果可以选择Python已有的内置的错误类型(比如ValueErrorTypeError),尽量使用Python内置的错误类型。