2.25. 處理錯誤

Python 中大多數的執行階段問題都會以例外(exception)的形式呈現 -- 這是一種具名且結構化的方式,用來回報發生了某些錯誤。ValueErrorTypeErrorKeyErrorOSErrorMemoryError 都是例子;每一個都是一個類別,而引發(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()
流程圖顯示通過 try / except / else / finally 的四種 可能路徑:成功會進入 else 然後 finally;被攔截的例外 會進入 except 然後 finally;未被攔截的例外會進入 finally 然後向外傳播。

finally 永遠都會執行。else 只在沒有例外的路徑上執行。

對於大多數的取得/釋放模式,請優先使用 情境管理器 而非 try / finally 組合 -- 由資源本身管理它自己的清理工作。

2.25.3. 常見的內建例外

以下是你會經常遇到的例外簡短清單:

  • ValueError -- 型別正確,但值錯誤(bytes([300]) -- 300 的型別正確,但超出了 0..255 這個有效的位元組範圍)。

  • TypeError -- 型別完全錯誤(len(42))。

  • KeyError -- dict 中缺少某個鍵。

  • IndexError -- 索引超出序列的結尾。

  • AttributeError -- 存取一個不存在的屬性("abc".foo)。

  • OSError -- 檔案系統或 I/O 失敗。

  • MemoryError -- 堆積(heap)耗盡。在記憶體受限的執行環境中,這在正常運作期間就可能發生 -- 而不只是在病態的情況下。