冲扬心法

笑声激发自强,发愤、图强、飞身向上

   :: 首页  ::  ::  ::  :: 管理

高级语言包括python一般都内置了一套try…catch…finally的错误处理机制:

>>> try:
...     print('try...')
...     r = 10 / 0
...     print('result:', r)
... except ZeroDivisionError as e:
...     print('except:', e)
... finally:
...     print('finally...')
...
try...
except: division by zero
finally...

如果认为某些代码可能会出错,可以用try来运行这段代码;

如果try的代码块出现错误,则try代码省下的代码不会继续执行,而是直接跳转到catch代码块,catch就是错误处理代码块(如果没有错误,则不执行)

如果还有finally代码块,则执行finally代码块。没有则不执行

我们看到代码执行 10 / 0 的时候出现了错误(0不能作为除数),下面测试没有错误的情况

>>> try:
...     print('try……')
...     r = 10 / 2
...     print('结果:%s' % r)
... except ZeroDivisionError as e:
...     print('发生了异常:',e)
... finally:
...     print('最后执行……')
...
try……
结果:5.0
最后执行……

如果try代码块可能出现多种错误类型,可以编写多个except代码块来处理;此外,如果没有发生错误,还可以在except代码块后面加上else语句,当没有错误的时候,会自动执行else语句:

>>> try:
...     print('开始:')
...     r = 10 / int('2')
...     print('结果:',r)
... except ValueError as e:
...     print('ValueError:',e)
... except ZeroDivisionError as e:
...     print('ZeroDivision:',r)
... else:
...     print('没有出错!')
... finally:
...     print('最后要执行的代码')
...
开始:
结果: 5.0
没有出错!
最后要执行的代码

万物皆对象,python的错误也是class,所有的错误类型都继承自BaseException,各个类型的错误之间可能会存在继承关系,比如UnicodeError是ValueError的子类,如果catch语句中同时出现了这两个错误,且UnicodeError在ValueError的后面处理的,那么永远都捕获不到UnicodeError。

下面是python中内置的常用错误类型继承关系:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      |    +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

使用try…catch…捕获错误一个好处就是,可以跨层调用,比如main()调用foo(),foo()调用bar(),而错误是在bar中出现的,最后我们只需要在main()中捕获就行:

>>> def foo(s):
...     return 10 / int(s)
...
>>> def bar(s):
...     return foo(s)*2
...
>>> def main():
...     try:
...             bar('0')
...     except Exception as e:
...             print('Error:',e)
...     finally:
...             print('最终要执行的代码')
...
>>> main()
Error: division by zero
最终要执行的代码

调用栈

如果没有捕捉错误,该错误就会一直往上抛,最后被python解释器捕获,并打印一条错误消息,然后退出程序。下面新建一个err.py文件:

def foo(s):
    return 10 / int(s)
def bar(s):
    return foo(s) * 2
def main():
    bar('0')

main()

执行结果:

PS E:\Python3.6.3\workspace> python err.py
Traceback (most recent call last):
  File "err.py", line 8, in <module>
    main()
  File "err.py", line 6, in main
    bar('0')
  File "err.py", line 4, in bar
    return foo(s) * 2
  File "err.py", line 2, in foo
    return 10 / int(s)
ZeroDivisionError: division by zero

上面的信息我们可以看到整个错误的函数调用栈:

错误第一行:

Traceback (most recent call last):

告诉我们以下是错误的跟踪信息。

第2~3行:

File "err.py", line 8, in <module>
    main()

告诉我们err.py执行中,main()出错了,在代码的第8行。

第4~5行:

File "err.py", line 6, in main
    bar('0')

告诉我们错误原因是第6行代码。

依次往下,最终告诉我们是foo函数出错了:

File "err.py", line 2, in foo
    return 10 / int(s)

这就是错误的源头,因为控制台打印了错误类型:

ZeroDivisionError: division by zero

这是个ZeroDivisionError,我们分析并不是Int(s)本身定义或者语法有错误,而是int(s)返回值为0,从而找到了源头。

上面说了当我们在程序中不捕获错误的时候,python解释器会在自动打印错误的堆栈,但是程序也会戛然而止。我们可以选择把错误堆栈打印出来,同时程序会继续执行下去。怎么操作呢?python内置的logging模块可以非常清楚的记录错误信息,新建一个err_logging.py文件:

import logging
def foo(s):
    return 10 / int(s)
def bar(s):
    return foo(s)*2
def main():
    try:
        bar('0')
    except Exception as e:
        logging.exception(e)

main()
print('最后执行了……')

执行结果:

ERROR:root:division by zero
Traceback (most recent call last):
  File "err_logging.py", line 8, in main
    bar('0')
  File "err_logging.py", line 5, in bar
    return foo(s)*2
  File "err_logging.py", line 3, in foo
    return 10 / int(s)
ZeroDivisionError: division by zero
最后执行了……

同样出错了,但是程序处理完错误信息后会继续执行。

因为错误对象就是class,其实我们自己也可以自定义错误用于抛出。

首先,应该定义一个错误的类,选择继承关系,然后用raise关键字抛出实例,创建一个err_raise.py文件:

class FooError(ValueError):
    pass
def foo(s):
    n = int(s)
    if n == 0 :
        raise FooError('非法的数值:%s' % s)
    return 10 / n

foo('0')

执行后,如果有错误,我们可以追踪到自己定义的错误:

PS E:\Python3.6.3\workspace> python err_raise.py
Traceback (most recent call last):
  File "err_raise.py", line 9, in <module>
    foo('0')
  File "err_raise.py", line 6, in foo
    raise FooError('非法的数值:%s' % s)
__main__.FooError: 非法的数值:0

有些时候我们会碰到一些当前代码不适合处理或者不能处理的错误,我可以选择记录下错误之后,在向上抛,这时在except代码块中加入raise语句。raise语句还可以将错误类型转化。

 

posted on 2018-03-15 13:26  风雨一肩挑  阅读(167634)  评论(0编辑  收藏  举报