Python 寫久了不免會經常遇到各種錯誤訊息,尤其是各式各樣的例外(Exception)異常:
- SyntaxError,這個排第一應該當之無愧 XD
- IndexError、NameError、ValueError
- UnboundLocalError
通常是在 Function 裡面先使用到了一個後面宣告的變數(例如x
),即使該變數和某 Global 變數同名,若就是想要修改 Global 的那一個也必須先宣告global x
。 - KeyboardInterrupt,例如使用者按了 Ctrl-C
- ⋯
詳細請參考 Python 內建的 Exception 列表。發生 Exception 的當下往往伴隨著反向追蹤 Traceback 程式碼指出問題所在,省下不少工程師們的時間。
Exception Handling
不過還是會遇到不得不好好面對 Exception 的時候,例如希望將字串轉為數字:
def str_to_int(text):
return int(text)
但如果輸入的 text
不是 10 進位的數字的話程式就會拋出 ValueError 例外,此時初學者可能會 try…except…
這樣寫:
def str_to_int(text):
try:
return int(text) # NOTICE: syntax error here won't get error
except:
return None
此範例很簡單所以不容易出問題,然而有些 Exception 的行為只是單純 Bug,如果無謂使用前面的做法就會錯過這些問題。
Recommended Syntax
因此較推薦的寫法是只抓取自己預期的例外情況:
def str_to_int(text):
try:
# when code here is complex, you may not want to catch all exceptions.
# that way you can find out bugs in a early stage of development
return int(text)
except ValueError:
return None
另外如果想要把例外狀況印出來可以設為一個變數使用:
def str_to_int(text):
try:
return int(text)
except ValueError as e:
print(e)
return None
Advanced Syntax
而如果需要抓多種例外狀況可以並列寫:
def str_to_int(text):
try:
return int(text)
except ValueError as e:
print(e)
return None
except NameError as e:
print(e)
return None
然後你發現發生例外狀況之後還是有些共通的程式要處理,於是可以加入 finally
Block:
def str_to_int(text):
try:
return int(text)
except ValueError as e:
print(e)
except NameError as e:
print(e)
finally:
return None
以上範例雖然有點殺雞用牛刀,但希望大家可以著重在 try…except…
寫法的部分。
User-Defined Exceptions
在撰寫自己的程式時,如果有條件需要跑出例外異常,建議的做法是自己定義一個新的 Exception 以免跟內建的例外異常混淆,並且還能針對自己客製化的例外做處理。
class MyError(Exception):
""" raise anytime I want to """
def str_to_int(text):
if text == "sakyouz": # just a condition shall raise an user-defined exception
raise MyError
# raise MyError("if you want to customize error message")
try:
return int(text)
except ValueError:
return None
try:
# this actually raise NameError because x is not defined,
# and since we have a good practice, Python tells us such problem.
str_to_int(x)
except MyError:
pass # you should do serious error handling here or at least logging
Suppress Exception
但如果不在意某個 Exception 的發生又覺得每次都要寫 try…except…
很麻煩,Python 內建了一個 Module 幫你:
from contextlib import suppress
with suppress(MyError):
x = str_to_int("sakyouz") # some codes may raise an error that we don't care
Assertion Handling
那 Assert 在幹嘛?Assert 不用處理嗎?還真不用,相信你也從來不會看到 Python 告訴你發生 Assert。
Assert 的條件是用來確保程式內部正確的運行,無關乎使用者也完全不預期會發生,如果遇到了 Assert 就必須視為 Bug。
承前面的例子,可以卡一個型別檢查的 Assert:
def str_to_int(text):
assert isinstance(text, str) # subclass of str also pass for isinstance
try:
return int(text)
except ValueError:
return None
如果要加入 Assert 訊息,就加逗點然後寫在後面:
def str_to_int(text):
assert isinstance(text, str), f"{typeof(text)=} shall be str or subclass of str."
try:
return int(text)
except ValueError:
return None