在程序運行過程中,總會遇到各種各樣的錯誤。
有的錯誤是程序編寫有問題造成的,比如本來應該輸出整數結果輸出了字符串,這種錯誤我們通常稱之爲bug,bug是必須修復的。
有的錯誤是用戶輸入造成的,比如讓用戶輸入email地址,結果得到一個空字符串,這種錯誤可以通過檢查用戶輸入來做相應的處理。
還有一類錯誤是完全無法在程序運行過程中預測的,比如寫入文件的時候,磁盤滿了,寫不進去了,或者從網絡抓取數據,網絡突然斷掉了。這類錯誤也稱爲異常,在程序中通常是必須處理的,否則,程序會因爲各種問題終止並退出。
Python內置了一套異常處理機制,來幫助我們進行錯誤處理。
此外,我們也需要跟蹤程序的執行,查看變量的值是否正確,這個過程稱爲調試。Python的pdb可以讓我們以單步方式執行代碼。
最後,編寫測試也很重要。有了良好的測試,就可以在程序修改後反覆運行,確保程序輸出符合我們編寫的測試。
錯誤處理機制
try
高級語言一般都內置一套try....except....finally
的錯誤處理機制。
try的一個例子:
try:
print ('try...')
r=10/0
print ('result:%s'%r)
except ZeroDivisionError as e:
print ('except:',e)
finally:
print('finally...')
print ('END')
如果我們認爲某些代碼可能出錯,就可以用try
來運行這段代碼,如果代碼出錯,則後續代碼不會繼續執行,而是直接跳轉至錯誤處理代碼,即except
語句塊處,執行完後,如果有finally
語句塊,則執行finally
語句塊,至此,執行完畢。
如果沒有錯誤發生,except
語句塊不會執行,但是finally
有的話,會被執行。(可以沒有finally
語句)。然後,程序繼續按照流程往下走。
若有多種類型的錯誤,可以使用多個except來捕獲不同類型的錯誤。
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')
另外可以在except
後面加一個else
,當沒有錯誤發生時,會自動執行else
語句。
Key point:
ZeroDivisionError
是一種可以被捕獲的錯誤。所有被捕獲的錯誤其實也是class,所有的錯誤類型都繼承自BaseException
,所以在使用except
時需要注意的是,它不但捕獲該類型的錯誤,還把其子類也“一網打盡”。比如:
try:
foo()
except ValueError as e:
print('ValueError')
except UnicodeError as e:
print('UnicodeError')
第二個except永遠也捕獲不到UnicodeError,因爲UnicodeError是ValueError的子類,如果有,也被第一個except給捕獲了。
- python3中except的語法爲
except ... as .. :
。但是python2中語法爲except ... , ..
。as用逗號代替。 - 使用
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...')
調用堆棧
若沒有try...except
語句捕獲錯誤,當程序遇到錯誤時,沒有將錯誤一直想上拋,最後被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還可以把錯誤記錄到日誌文件裏,方便事後排查。
拋出錯誤
FIXME:raise