天天看點

Python 錯誤處理

>>> try:

...    print('try...')

...    r = 10 / 0

...    print('result:', r)

... except ZeroDivisionError as e:

...    print('except:', e)

... finally:

...    print('finally...')

...    print('END')

...

try...

except: division by zero

finally...

END

當我們認為某些代碼可能會出錯時,就可以用try來運作這段代碼,如果執行出錯,則後續代碼不會繼續執行,而是直接跳轉至錯誤處理代碼,即except語句塊,執行完except後,如果有finally語句塊,則執行finally語句塊,至此,執行完畢。

finally語句塊無論是否執行except,都會被執行的。

同時定義多個except

...    r = 10 / int('a')

... except ValueError as e:    --異常1

...    print('ValueError:', e)

... except ZeroDivisionError as e:   --異常2

... else:   --無異常時執行else

...    print('no error')

ValueError: invalid literal for int() withbase 10: 'a'

跨越多層調用

使用try...except捕獲錯誤還有一個巨大的好處,就是可以跨越多層調用,比如函數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('finally...')

>>> main()

Error: division by zero

隻要在合适的層次去捕獲錯誤就可以了。

[root@daidai python]# cat err.py

#!/bin/bash/python

# -*- coding:utf-8 -*-

def foo(s):

   return 10 / int(s)

def bar(s):

   return foo(s) * 2

def main():

   bar('0')

main()

[root@daidai python]# python err.py

Traceback (most recent call last):

 File "err.py", line 13, in <module>

   main()

 File "err.py", line 11, in main

 File "err.py", line 8, in bar

 File "err.py", line 5, in foo

ZeroDivisionError: division by zero    --最後便是錯誤的根源

捕捉錯誤,把錯誤堆棧記錄,讓程式繼續運作。

Python内置的logging子產品可以非常容易地記錄錯誤資訊。

[root@daidai python]# cat err_logging.py

#!/usr/bin/python

import logging

   try:

       bar('0')

   except Exception as e:

       logging.exception(e)

print('END')

[root@daidai python]# python err_logging.py

ERROR:root:division by zero

 File "err_logging.py", line 14, in main

    bar('0')

 File "err_logging.py", line 10, in bar

 File "err_logging.py", line 7, in foo

ZeroDivisionError: division by zero

盡量使用python内置的錯誤類型。

錯誤是class,捕獲一個錯誤就是捕獲到該class的一個執行個體。

定義一個錯誤class,選擇好繼承關系。

>>> class FooError(ValueError):

...    pass

...    n = int(s)

...    if n == 0:

...         raise FooError('invalid value: %s' % s)

...    return 10 / n

>>> foo('0')

 File "<stdin>", line 1, in <module>

 File "<stdin>", line 4, in foo

__main__.FooError: invalid value: 0     --自己定義的錯誤

另一種錯誤處理方式——錯誤上抛

...         raise ValueError('invalid value: %s' %s)    --python内置錯誤

...    return 10 /n

>>> def bar():

...        foo('0')

...    except ValueError as e:

...        print('ValueError')

...        raise    --raise語句如果不帶參數,就會把目前錯誤原樣抛出。

>>> bar()

ValueError

 File "<stdin>", line 3, in bar

ValueError: invalid value: 0

其實這種錯誤處理方式不但沒病,而且相當常見。捕獲錯誤目的隻是記錄一下,便于後續追蹤。但是,由于目前函數不知道應該怎麼處理該錯誤,是以,最恰當的方式是繼續往上抛,讓頂層調用者去處理。好比一個員工處理不了一個問題時,就把問題抛給他的老闆,如果他的老闆也處理不了,就一直往上抛,最終會抛給CEO去處理。

在except中raise一個Error,還可以把一種類型的錯誤轉化成另一種類型

...    10 / 0

... except ZeroDivisionError:

...    raise ValueError('input error')

 File "<stdin>", line 2, in <module>

During handling of the above exception,another exception occurred:

 File "<stdin>", line 4, in <module>

ValueError: input error