raise … 和 raise … from … 的區別
raise …:
During handling of the above exception, another exception occurred:
raise … from …:
The above exception was the direct cause of the following exception:
使用
沒有 from
是接近錯誤的使用方式。
比如:
try:
raise IndexError("in try")
except IndexError as err:
raise RuntimeError(str(err))
與:
def hanlder_error():
raise RuntimeError("error by accident")
try:
raise IndexError("in try")
except IndexError as err:
hanlder_error()
raise RuntimeError(str(err)) from err
這是兩段有着不同含義的代碼。
運行結果對比:
不看錯誤提示消息中的代碼的話,“字面上”是一個意思。
刪掉錯誤提示的代碼之後,提示信息除了有指出第二個 error 發生的所在位置(在函數裏面)之外,提示信息的含義完全一樣:
這個提示信息的含義都表示:第二個異常是在 except
/捕獲異常 內部(再次)發生了異常(During handling of the above exception, another exception occurred
)。
但是實際情況是:
-
第二段代碼確實符合提示的錯誤信息(
RuntimeError
是在捕獲IndexError
異常處理時另外發生的異常)。 -
第一段完全不是,第一段代碼的第二個異常並非是在捕獲異常內部(
except
中)再次發生異常,而本意是想將try
內發生的異常(通過另外一種異常類來表示)向更高級調用拋出。
如果使用 raise ... from ...
則錯誤提示消息含義完全不同:
try:
raise IndexError("in try")
except IndexError as err:
raise RuntimeError(str(err)) from err
當我們看到 The above exception was the direct cause of the following exception
則知道,這句話下面的異常本質上和這句話上面的異常是同一個。這句話下面的異常加上這句話本身表示了該異常來自 try
內部,而非 except
內。
而 During handling of the above exception, ...
這句表示了這句話下面的異常來自 except
內;即真正意思是表示了此時發生兩個異常。
所以我們需要使用 raise ... from ...
這樣的寫法,如果 except
內部真的沒有異常發生的話,錯誤提示也是表達了只有一個異常,這個異常進一步回溯到 try
內部發生的異常。
而 raise ...
這麼直接編寫的話,雖然在代碼作用上沒有什麼差別,但是含義上有區別,它表示了 try
內部發生了異常,但是在 except
內部處理的時候,又發生了另一個異常;這和實際情況是不符合的。
總結
raise
本身就是拋出異常的作用(含義)。
所以當我們想要在 except
內部拋出另外一個異常,就使用 raise <SomeError>
但是如果我們想要將原本來自 try
內部的異常,在 except
中繼續(往上)拋出的話,要嘛直接使用 raise
,即:
try:
raise IndexError("in try")
except IndexError as err:
...do something...
raise
又或者我們想要對這個異常換一種異常類(換一種表示異常含義,比如自己定義的異常類等),
那麼就應當使用 raise ... from <last_error_instance>
, 即:
try:
raise IndexError("in try")
except IndexError as err:
...do something...
raise RuntimeError("the right way to continue raise") from err