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
但是如果我们想要将原本来自 try
内部的异常,在 except
中继续(往上)抛出的话,要嘛直接使用 raise
,即:
try:
raise IndexError("in try")
except IndexError as err:
...do something...
raise
又或者我们想要对这个异常换一种异常类(换一种表示异常含义,比如自己定义的异常类等),
那么就应当使用 raise ... from
, 即:
try:
raise IndexError("in try")
except IndexError as err:
...do something...
raise RuntimeError("the right way to continue raise") from err