在執行程式時,可能會遇到一些問題導致程式執行失敗,這時候 python 就會丟出 exception,並且印出 stack trace,可以看是程式執行到哪邊發生錯誤.
>>> 1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
如果執行的是一個檔案,會說是檔案哪一行錯了.
> python3 calc.py
5
6
8
Traceback (most recent call last):
File "calc.py", line 4, in <module>
print(1/0)
ZeroDivisionError: division by zero
寫程式時通常會有兩種方式來處理程式異常,一種就是先把會出現錯誤的情況避免掉也稱作 LBYL (look before you leap),另一種是就等發生了再來處理也稱作 EAFP (easier to ask for forgiveness than permission).
使用 LBYL 的話可以將上面的程式改成,用條件控制先判斷 num2 不為 0 才除,
>>> def division(num1,num2):
... if num2 != 0:
... print(num1 / num2)
... else:
... print(0)
...
>>> division(1,0)
0
>>> division(1,2)
0.5
可是假設將 None 丟進去,又會出錯了.所以使用 LBYL 的方式,需要考慮到很多情況來避免出錯.
>>> division(1,None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in division
TypeError: unsupported operand type(s) for /: 'int' and 'NoneType'
使用 EAFP 的方式,將可能會出錯的地方放在 try 區塊裡,
>>> def division(num1,num2):
... try:
... print(num1 / num2)
... except ZeroDivisionError as e:
... print(0)
...
>>> division(1,0)
0
>>> division(1,2)
0.5
>>> division(1,None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in division
TypeError: unsupported operand type(s) for /: 'int' and 'NoneType'
由於上面只有捕捉 ZeroDivisionError 的錯誤,所以如果不屬於該類型的錯誤,程式還是會執行錯誤,所以可以在下面再多一個比 ZeroDivisionError 層級更高的錯誤,就可以捕捉到.
>>> def division(num1,num2):
... try:
... print(num1 / num2)
... except ZeroDivisionError as e:
... print(0)
... except Exception as e:
... print('wrong number')
...
>>> division(1,None)
wrong number
如果補到例外後,想用不同的例外丟給呼叫該 function 的使用者,可以使用 raise 這關鍵字,像下面的範例捕捉到了 TypeError 會再把 RuntimeError 也拋出來給使用者.而 finally 區塊是不管有沒有發生例外,最後都會被執行的區塊.
>>> def division(num1,num2):
... try:
... print(num1 / num2)
... except ZeroDivisionError as e:
... print(0)
... except Exception as e:
... raise RuntimeError('wrong number')
... finally:
... print('do something...')
...
>>> division(1,2)
0.5
do something...
>>> division(1,0)
0
do something...
>>> division(1,None)
do something...
Traceback (most recent call last):
File "<stdin>", line 3, in division
TypeError: unsupported operand type(s) for /: 'int' and 'NoneType'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in division
RuntimeError: wrong number
如果要定義自己的 exception,只需要將 class 繼承 Exception 即可.
>>> class CustomError(Exception):
... pass
...
就可以使用了.
>>> def division(num1,num2):
... try:
... print(num1 / num2)
... except ZeroDivisionError as e:
... print(0)
... except Exception as e:
... raise CustomError('Custom wrong error')
...
>>> division(1,None)
Traceback (most recent call last):
File "<stdin>", line 3, in division
TypeError: unsupported operand type(s) for /: 'int' and 'NoneType'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in division
__main__.CustomError: Custom wrong error