Python中的raise
和raise from
有什么区别?
try:
raise ValueError
except Exception as e:
raise IndexError
从而产生
Traceback (most recent call last):
File "tmp.py", line 2, in <module>
raise ValueError
ValueError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "tmp.py", line 4, in <module>
raise IndexError
IndexError
和
try:
raise ValueError
except Exception as e:
raise IndexError from e
从而产生
Traceback (most recent call last):
File "tmp.py", line 2, in <module>
raise ValueError
ValueError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "tmp.py", line 4, in <module>
raise IndexError from e
IndexError
3条答案
按热度按时间ffx8fchx1#
不同之处在于,当你使用
from
时,*__cause__
属性 * 被设置,并且消息声明异常是 * 由 * 直接引起的。如果你省略了from
,那么就不会设置__cause__
,但是 *__context__
属性 * 也可能被设置,然后回溯将上下文显示为 * 在处理其他事件期间 *。如果在异常处理程序中使用
raise
,则会设置__context__
;如果你在其他地方使用了raise
,也没有设置__context__
。如果设置了
__cause__
,则在异常上也设置了__suppress_context__ = True
标志;当__suppress_context__
设置为True
时,打印追溯时将忽略__context__
。当从一个异常处理程序中引发时,如果你 * 不 * 想显示上下文(不想在处理另一个异常发生 * 消息期间显示 *),那么使用
raise ... from None
将__suppress_context__
设置为True
。换句话说,Python在异常上设置了一个 context,这样你就可以内省一个异常是在哪里引发的,让你看看是否有另一个异常被它替换了。你也可以给一个异常添加一个 cause,让回溯对另一个异常显式显示(使用不同的措辞),并且上下文被忽略(但在调试时仍然可以进行内省)。使用
raise ... from None
可以禁止打印上下文。请参阅
raise
语句文档:from
子句用于异常链接:如果给定,第二个 expression 必须是另一个异常类或示例,然后将作为__cause__
属性(可写)附加到引发的异常。如果引发的异常未被处理,则两个异常都将被打印:如果在异常处理程序或
finally
子句中引发异常,则类似的机制会隐式工作:然后将前一个异常附加为新异常的__context__
属性:有关附加到异常的上下文和原因信息的详细信息,请参阅内置异常文档。
vnzz0bqm2#
2005年,PEP 3134, Exception Chaining and Embedded Tracebacks引入了异常链:
raise EXCEPTION
或隐式提升(__context__
属性);raise EXCEPTION from CAUSE
(__cause__
属性)。动机
在处理一个异常(异常A)的过程中,可能会发生另一个异常(异常B)。在今天的Python(版本2.4)中,如果发生这种情况,异常B会向外传播,异常A会丢失。为了调试问题,了解这两个异常是很有用的。
__context__
属性会自动保留此信息。有时候,异常处理程序有意地重新引发一个异常是很有用的,可以提供额外的信息,也可以将异常转换为另一种类型。
__cause__
属性提供了一种显式的方法来记录异常的直接原因。[...]
隐式异常链
下面是一个示例来说明
__context__
属性:调用
compute(0, 0)
会导致ZeroDivisionError
。compute()
函数捕获此异常并调用log(exc)
,但log()
函数在尝试写入未打开的文件时也会引发异常。在今天的Python中,
compute()
的调用者会被抛出一个IOError
,ZeroDivisionError
丢失了,通过建议的修改,IOError
的示例有了一个额外的__context__
属性来保留ZeroDivisionError
。[...]
显式异常链
异常对象上的
__cause__
属性总是初始化为None
。它由raise
语句的新形式设置:这相当于:
在下面的示例中,数据库提供了几种不同类型的存储的实现,文件存储是其中的一种。数据库设计人员希望错误以
DatabaseError
对象的形式传播,这样客户端就不必知道特定于存储的详细信息,但又不希望丢失底层的错误信息。如果对
open()
的调用引发异常,则问题将被报告为DatabaseError
,其__cause__
属性显示IOError
是原始原因。增强报表
默认的异常处理器将被修改为报告链式异常。通过
__cause__
和__context__
属性遍历异常链,__cause__
优先。为了与追溯的时间顺序保持一致,最后显示最近引发的异常;也就是说,显示从最内层异常的描述开始,并将链备份到最外层异常。追溯的格式与通常一样,其中一行:上述异常是导致以下异常的直接原因:
或
在处理上述异常的过程中,又出现了一个异常:
在回溯之间,取决于它们分别是由
__cause__
还是__context__
链接的。下面是过程的草图:[...]
2012年,PEP 415, Implement Context Suppression with Exception Attributes引入了显式
raise EXCEPTION from None
(__suppress_context__
属性)的异常上下文抑制。提案
将引入
BaseException
上的新属性__suppress_context__
。每当设置__cause__
时,__suppress_context__
将被设置为True
。特别地,raise exc from cause
语法将exc.__suppress_context__
设置为True
。异常打印代码将检查该属性以确定是否打印上下文和原因。__cause__
将恢复其原始用途和价值。具有
print_line_and_file
异常属性的__suppress_context__
具有优先级。总而言之,
raise exc from cause
相当于:其中
exc.__cause__ = cause
隐式设置exc.__suppress_context__
。因此,在PEP 415中,PEP 3134中为缺省异常处理程序(其任务是报告异常)给出的过程的草图变为如下:
cpjpxq1n3#
最短的答案. PEP-3134说明了一切。
raise Exception from e
设置新异常的__cause__
字段。来自同一PEP的更长答案:
__context__
字段将被隐式地设置为except:
块内的原始错误,除非被告知不与__suppress_context__ = True
一起设置。__cause__
就像上下文一样,但必须使用from
语法显式设置except
块中调用raise
时,traceback
总是会链接。你可以通过a)吞下一个异常except: pass
或直接修改sys.exc_info()
来摆脱回溯。冗长的答案
将导致以下输出: