錯(cuò)誤處理
在程序運(yùn)行的過程中,如果發(fā)生了錯(cuò)誤耗式,可以事先約定返回一個(gè)錯(cuò)誤代碼
用錯(cuò)誤碼來表示是否出錯(cuò)十分不便胁住,因?yàn)楹瘮?shù)本身應(yīng)該返回的正常結(jié)果和錯(cuò)誤碼混在一起,造成調(diào)用者必須用大量的代碼來判斷是否出錯(cuò)
try...except...finally...
錯(cuò)誤處理機(jī)制
如果沒有錯(cuò)誤發(fā)生刊咳,可以在except語句塊后面加一個(gè)else彪见,當(dāng)沒有錯(cuò)誤發(fā)生時(shí),會(huì)自動(dòng)執(zhí)行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')
Python的錯(cuò)誤其實(shí)也是class娱挨,所有的錯(cuò)誤類型都繼承自 BaseException
常見的錯(cuò)誤類型和繼承關(guān)系:常見的錯(cuò)誤類型和繼承關(guān)系
不需要在每個(gè)可能出錯(cuò)的地方去捕獲錯(cuò)誤企巢,只要在合適的層次去捕獲錯(cuò)誤就可以了。這樣一來让蕾,就大大減少了寫try...except...finally的麻煩
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...')
調(diào)用堆棧
記錄錯(cuò)誤
Python內(nèi)置的logging模塊可以非常容易地記錄錯(cuò)誤信息:
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:
# 同樣是出錯(cuò)浪规,但程序打印完錯(cuò)誤信息后會(huì)繼續(xù)執(zhí)行,并正常退出
logging.exception(e)
main()
print('END')
通過配置探孝,logging還可以把錯(cuò)誤記錄到日志文件里笋婿,方便事后排查
拋出錯(cuò)誤
如果要拋出錯(cuò)誤,首先根據(jù)需要顿颅,可以定義一個(gè)錯(cuò)誤的class缸濒,選擇好繼承關(guān)系,然后,用raise語句拋出一個(gè)錯(cuò)誤的實(shí)例:
class FooError(ValueError):
pass
def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value: %s' % s)
return 10 / n
foo('0')
另一種錯(cuò)誤處理的方式:
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!')
# 捕獲錯(cuò)誤后庇配,又把錯(cuò)誤通過raise語句拋出去
raise
bar()
raise語句如果不帶參數(shù)斩跌,就會(huì)把當(dāng)前錯(cuò)誤原樣拋出
在except中raise一個(gè)Error,還可以把一種類型的錯(cuò)誤轉(zhuǎn)化成另一種類型:
try:
10 / 0
except ZeroDivisionError:
raise ValueError('input error!')
調(diào)試
斷言
凡是用print()來輔助查看的地方捞慌,都可以用斷言(assert)來替代:
def foo(s):
n = int(s)
# 當(dāng)表達(dá)式 n != 0 為 True 時(shí)耀鸦,程序才能繼續(xù)下去,否則斷言失敗
# 如果斷言失敗啸澡,assert語句本身就會(huì)拋出 AssertionError
assert n != 0, 'n is zero!'
return 10 / n
def main():
foo('0')
啟動(dòng)Python解釋器時(shí)可以用-O參數(shù)來關(guān)閉assert:
python3 -O err.py
logging
把print()替換為logging是第3種方式袖订,和assert比,logging不會(huì)拋出錯(cuò)誤嗅虏,而且可以輸出到文件:
import logging
logging.basicConfig(level=logging.INFO)
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)
logging
允許你指定記錄信息的級(jí)別洛姑,有debug,info皮服,warning楞艾,error等幾個(gè)級(jí)別,當(dāng)我們指定 level=INFO 時(shí)龄广,logging.debug就不起作用了
pdb
第4種方式是啟動(dòng)Python的調(diào)試器pdb硫眯,讓程序以單步方式運(yùn)行,可以隨時(shí)查看運(yùn)行狀態(tài)