先看一個例子:
>>> def first_example(n):
... return / n
...
>>> first_example()
>>> first_example()
Traceback (most recent call last):
File "<stdin>", line , in <module>
File "<stdin>", line , in first_example
ZeroDivisionError: division by zero
>>>
從例子中得知,n不能為,否則将會報一個ZeroDivisionError
(除數不能為)的錯誤,我們需要對錯誤進行處理,比如說當發生
的時候我們可以事先約定一個錯誤代碼,這樣當以後出錯的時候就可
出錯的原因。而上面的ZeroDivisionError就是系統内定的一個
錯誤代碼。
通常在python中我們使用,以下格式來定義一個錯誤代碼:
>>> def first_example(n):
... try:
... return / n
... except ZeroDivisionError as error:
... print('錯誤資訊:',error)
...
>>> first_example()
>>> first_example()
錯誤資訊: division by zero
>>>
将你需要執行的代碼塊(該代碼塊運作時可能出錯)放
在try語句裡面,将出錯類型放在except後面,并給其
賦一個引用變量,下面的print是列印錯誤資訊,前半
部分是自定義字元串。
看第二個例子,函數之間的 錯誤處理:
>>> def too(n):
... return / n
...
>>> def foo(n):
... return too(n)
...
>>> def second_example(n):
... try:
... print(foo(n))
... except ZeroDivisionError as a:
... print('出錯原因:',a)
...
>>> second_example()
>>> second_example()
出錯原因: division by zero
>>>
我們繼續往下看,我們在上面的例子上進行修改:
>>> def too(n):
... return / n
...
>>> def foo(n):
... return too(n) *
...
>>> def second_example(n):
... try:
... print(foo(n))
... except ZeroDivisionError as a:
... print('出錯原因:',a)
... else:
... print('看看我的出現規則')
...
>>>
>>> second_example()
看看我的出現規則
>>> second_example()
出錯原因: division by zero
不難看出,else語句塊是在沒有出錯的時候執行
出錯的時候就不再執行
我們再進行修改一下 :
>>> def too(n):
... return / n
...
>>> def foo(n):
... return too(n) *
...
>>> def second_example(n):
... try:
... print(foo(n))
... except ZeroDivisionError as a:
... print('出錯原因:',a)
... finally:
... print('我是finally代碼塊')
...
>>> second_example()
我是finally代碼塊
>>> second_example()
出錯原因: division by zero
我是finally代碼塊
可以看出,不管代碼有沒有出錯,finally代碼塊都會執行
我們再增加一點東西:
>>> def too(n):
... return int(n)
...
>>> def foo(s):
... a = s.split('+')
... b = map(too,a)
... return reduce(lambda x, y: x + y , b)
>>> def main(q,w):
... try :
... print( / q)
... print(w + '= ',foo(w))
... except ZeroDivisionError as error:
... print('錯誤資訊:',error)
... except ValueError as error:
... print('錯誤資訊:',error)
... finally:
... print('代碼執行完畢')
...
>>> main(,'100 + 23 + 45')
+ + =
代碼執行完畢
>>> main(,'100 + 23 + 45')
錯誤資訊: division by zero
代碼執行完畢
>>> main(,'100 + 23 + 4.5')
錯誤資訊: invalid literal for int() with base : ' 4.5'
代碼執行完畢
>>> main(,'100 + 23 + 4.5')
錯誤資訊: division by zero
代碼執行完畢
我們還是将需要執行但可能出錯的代碼放在try
語句中,但我們有兩個except,因為我們會猜測
需要執行的代碼可能不止報一個錯誤。是以我們可
以寫多個except
python 錯誤也是class,所有的錯誤類型都繼承自
BaseException,是以在寫except的時候我們應該
注意,如果有多個except,父類錯誤類型在前面的
except中,子類錯誤類型在後面的except中,那當
出現子類錯誤類型 的時候,報的是父類錯誤類型,
子類錯誤類型永遠不會有執行的機會,舉個例子:
>>> def third_example(n):
... try:
... return / n
... except BaseException as a:
... print('BaseException',a)
... except ZeroDivisionError as error:
... print('ZeroDivisionError',error)
...
>>> third_example()
BaseException division by zero
是以為了避免這種情況,我們應該把表示範
圍小的子類錯誤類型放前面。
常見的錯誤類型和繼承關系進入這個連結:
常見錯誤類型和繼承關系
錯誤處理的方式還有一種叫斷言(assert):
>>> def odd(n):
... assert n
... return / n
...
>>> def fourth_example(n):
... print(odd(n))
...
>>> fourth_example()
>>> fourth_example()
Traceback (most recent call last):
File "<stdin>", line , in <module>
File "<stdin>", line , in fourth_example
File "<stdin>", line , in odd
AssertionError
>>>
斷言的文法是,當斷言後面的語句為真時,繼續往下執行,
當為假時就會報一個AssertionError的錯誤提示
我們再來看一下assert的更新版本:
>>> def odd(n):
... if n == :
... raise ValueError('0不能做除數')
... return / n
...
>>> def fourth_example(n):
... print(odd(n))
>>> fourth_example()
>>> fourth_example()
Traceback (most recent call last):
File "<stdin>", line , in <module>
File "<stdin>", line , in fourth_example
File "<stdin>", line , in odd
ValueError: 不能做除數
我們使用了raise關鍵字,當判斷除數為時
我們就會執行raise所在的語句,
我們在增加點佐料:
def odd(n):
if n == :
raise ZeroDivisionError('0不能做除數')
return / n
def fourth_example(n):
try:
print(odd(n))
except ZeroDivisionError as a:
print('錯誤提示:',a)
fourth_example(int(input('請輸入一個數字:')))
請輸入一個數字:
請輸入一個數字:
錯誤提示: 不能做除數
我的了解是 raise可以改變錯誤類型和後面的錯誤提示的内容
不加raise時的提示資訊是:
請輸入一個數字:
錯誤提示: division by zero
我們再修改一下
def odd(n):
if n == :
raise ZeroDivisionError('0不能做除數')
return / n
def fourth_example(n):
try:
print(odd(n))
except ValueError as a:
print('錯誤提示:',a)
fourth_example(int(input('請輸入一個數字:')))
請輸入一個數字:
Traceback (most recent call last):
File "G:/Pyproject/pachong/pachong.py", line , in <module>
fourth_example(int(input('請輸入一個數字:')))
File "G:/Pyproject/pachong/pachong.py", line , in fourth_example
print(odd(n))
File "G:/Pyproject/pachong/pachong.py", line , in odd
raise ZeroDivisionError('0不能做除數')
ZeroDivisionError: 不能做除數
結果并沒有except裡面的語句,隻有raise
在工作中我們有時候會這麼做:
def foo(s):
n = int(s)
if n==:
raise ValueError('invalid value: %s' % s)
return / n
def bar():
try:
foo('0')
except ValueError as e:
print('ValueError!')
raise
bar()
運作一下:
Traceback (most recent call last):
ValueError!
File "G:/Pyproject/pachong/pachong.py", line , in <module>
bar()
File "G:/Pyproject/pachong/pachong.py", line , in bar
foo('0')
File "G:/Pyproject/pachong/pachong.py", line , in foo
raise ValueError('invalid value: %s' % s)
ValueError: invalid value:
我們會捕獲錯誤在抛出錯誤,捕獲錯誤目的隻是記錄一下,
便于後續追蹤。但是,由于目前函數不知道應該怎麼處理該錯誤,
是以,最恰當的方式是繼續往上抛,讓頂層調用者去處理。好比
一個員工處理不了一個問題時,就把問題抛給他的老闆,如果他
的老闆也處理不了,就一直往上抛,最終會抛給CEO去處理。
因為錯誤是class,捕獲一個錯誤就是捕獲到該class的
一個執行個體。是以,錯誤并不是憑空産生的,而是有意創
建并抛出的。Python的内置函數會抛出很多類型的錯誤
,我們自己編寫的函數也可以抛出錯誤。
如果要抛出錯誤,首先根據需要,可以定義一個錯誤的
class,選擇好繼承關系,然後,用raise語句抛出一個錯
誤的執行個體:
class FooError(ValueError):
pass
def foo(s):
n = int(s)
if n==:
raise FooError('invalid value: %s' % s)
return / n
foo('0')
執行一下:
Traceback (most recent call last):
File "err_throw.py", line , in <module>
foo('0')
File "err_throw.py", line , in foo
raise FooError('invalid value: %s' % s)
__main__.FooError: invalid value:
最後我們在說一下記錄錯誤,Python内置的logging
子產品可以非常容易地記錄錯誤資訊:
import logging
def foo(s):
return / int(s)
def bar(s):
return foo(s) *
def main():
try:
bar('0')
except Exception as e:
logging.exception(e)
main()
print('END')
執行一下:
ERROR:root:division by zero
Traceback (most recent call last):
File "err_logging.py", line , in main
bar('0')
File "err_logging.py", line , in bar
return foo(s) *
File "err_logging.py", line , in foo
return / int(s)
ZeroDivisionError: division by zero
END
你會發現,錯誤後面的語句也可以執行。
通過配置,logging還可以把錯誤記錄到日志
檔案裡,友善事後排查,這些我們以後都會接觸。