python錯誤處理

try…except…finally…

http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143191375461417a222c54b7e4d65b258f491c093a515000zzzz


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

將可能錯誤的代碼放在try中測試,如果真的錯誤將直接跳轉到except語句塊。無論有沒有發生錯誤,如果有finally就執行finally,也可以沒有finally語句。
運行結果

try...
except: division by zero
finally...
END

錯誤本身是一個類(class),所有的錯誤類型繼承自BaseException,可以用多個except捕獲不同的Error
可以在except後加elseexcept沒有執行時執行else

try:
    print('try...')
    r = 10 / int('2')
    print('result:', r)
except ValueError as e:
    print('ValueError:', e)
except ZeroDivisionError as e:
    print('ZeroDivisionError:', e)
else:
    print('no error!')
finally:
    print('finally...')
print('END')

由於繼承關係,當捕獲父類的錯誤時,子類將被忽略

try:
    foo()
except ValueError as e:
    print('ValueError')
except UnicodeError as e:
    print('UnicodeError')

第二個except永遠也捕獲不到UnicodeError,因爲UnicodeErrorValueError的子類,如果有,也被第一個except給捕獲了。
常見錯誤類型和繼承關係

使用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...')

不需要在foobar函數中去捕獲錯誤

調用堆棧

如果錯誤沒有被捕獲,它就會一直往上拋,最後被Python解釋器捕獲,打印一個錯誤信息,然後程序退出。

記錄錯誤

既然我們能捕獲錯誤,就可以把錯誤堆棧打印出來,然後分析錯誤原因,同時,讓程序繼續執行下去。
Python內置的logging模塊可以非常容易地記錄錯誤信息。

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('END')

同樣是出錯,但程序打印完錯誤信息後會繼續執行,並正常退出。
通過配置,logging還可以把錯誤記錄到日誌文件裏,方便事後排查。

拋出錯誤raise

如果要拋出錯誤,首先根據需要,可以定義一個錯誤的class,選擇好繼承關係,然後,用raise語句拋出一個錯誤的實例:

class FooError(ValueError):
    pass

def foo(s):
    n = int(s)
    if n==0:
        raise FooError('invalid value: %s' % s)
    return 10 / n

foo('0')

只有在必要的時候才定義我們自己的錯誤類型,儘量使用Python內置的錯誤類型。

另一種錯誤處理

def foo(s):
    n = int(s)
    if n==0:
        raise ValueError('invalid value: %s' % s)
    return 10 / n

def bar():
    try:
        foo('0')
    except ValueError as e:
        print('ValueError!')
        raise

bar()

bar()函數中,打印一個ValueError!後,又把錯誤通過raise語句拋出去
捕獲錯誤目的只是記錄一下,便於後續追蹤。但是,由於當前函數不知道應該怎麼處理該錯誤,所以,最恰當的方式是繼續往上拋,讓頂層調用者去處理。好比一個員工處理不了一個問題時,就把問題拋給他的老闆,如果他的老闆也處理不了,就一直往上拋,最終會拋給CEO去處理。

raise語句如果不帶參數,就會把當前錯誤原樣拋出。此外,在except中raise一個Error,還可以把一種類型的錯誤轉化成另一種類型:

try:
    10 / 0
except ZeroDivisionError:
    raise ValueError('input error!')

上面有exceptraise與直接打印print錯誤的區別主要是raise可以返回更詳細的錯誤信息

發佈了23 篇原創文章 · 獲贊 9 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章