2.25. 處理錯誤¶
Python 中大多數的執行階段問題都會以例外(exception)的形式呈現 -- 這是一種具名且結構化的方式,用來回報發生了某些錯誤。ValueError、TypeError、KeyError、OSError、MemoryError 都是例子;每一個都是一個類別,而引發(raising)其中一個會停止目前的呼叫,並在周圍的程式碼中尋找處理器。
2.25.1. try / except¶
用 try 包住一段程式碼,以攔截它所引發的任何例外:
try:
n = int(input_text)
except ValueError:
n = 0
如果 int(...) 轉換失敗,控制流程會跳到 except 區塊,而不是讓錯誤繼續向外傳播。如果 input_text 是一個有效的整數字串,except 區塊就會被略過。
單一個 try 可以有多個 except 區塊,各自攔截不同種類的錯誤:
try:
value = data[key]
except KeyError:
value = None
except TypeError:
value = -1
Python 會依序比對;第一個符合其例外類別的就會處理該問題。攔截 Exception(幾乎所有例外的基底類別)能處理任何錯誤;請將其保留給程式的最外層,也就是在別無選擇而只能讓程式崩潰的地方使用。
警告
裸 except:(關鍵字後沒有接類別)也會攔截 KeyboardInterrupt -- 也就是當你按下 IDE 的停止按鈕來中斷正在執行的指令碼時,IDE 所送出的例外。被裸 except: pass 包住的迴圈會吞掉這個中斷並繼續執行,導致除了重新上電之外沒有其他方法可以停止指令碼。
當你確實需要廣泛攔截時,請優先使用 except Exception: 而非裸 except:。KeyboardInterrupt 繼承自 BaseException,而非 Exception,因此 except Exception: 會讓停止按鈕保持可用。
2.25.1.1. 檢視例外¶
若要讀取附加在例外上的訊息,請用 as 為它命名:
try:
f = open("data.txt")
except OSError as e:
print("could not open file:", e)
由 as 繫結的變數僅在 except 區塊內有效。
2.25.2. else 與 finally¶
try 區塊有兩個選用的附加部分。
else 只在 try 順利完成而未引發例外時執行:
try:
value = compute()
except ValueError:
print("bad input")
else:
print("got", value)
把「成功時要做的事」放進 else 能讓 try 區塊保持精簡 -- 只有可能失敗的那一行才屬於 try。
finally 在最後無論如何都會執行 -- 不論 try 是成功、引發例外並被處理,還是引發例外且即將向外傳播:
try:
do_work()
finally:
cleanup()
finally 永遠都會執行。else 只在沒有例外的路徑上執行。¶
對於大多數的取得/釋放模式,請優先使用 情境管理器 而非 try / finally 組合 -- 由資源本身管理它自己的清理工作。
2.25.3. 常見的內建例外¶
以下是你會經常遇到的例外簡短清單:
ValueError-- 型別正確,但值錯誤(bytes([300])-- 300 的型別正確,但超出了 0..255 這個有效的位元組範圍)。TypeError-- 型別完全錯誤(len(42))。IndexError-- 索引超出序列的結尾。AttributeError-- 存取一個不存在的屬性("abc".foo)。OSError-- 檔案系統或 I/O 失敗。MemoryError-- 堆積(heap)耗盡。在記憶體受限的執行環境中,這在正常運作期間就可能發生 -- 而不只是在病態的情況下。