Python “raise from” usage
Python中raise和raise from之间有什么区别?
1 2 3 4
| try:
raise ValueError
except Exception as e:
raise IndexError |
产量
1 2 3 4 5 6 7 8 9 10 11
| 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 |
和
1 2 3 4
| try:
raise ValueError
except Exception as e:
raise IndexError from e |
产量
1 2 3 4 5 6 7 8 9 10 11
| 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 |
-
你读过PEP-3134吗?
-
现在使用raise IndexError from None,比方说。
-
嘿。 raise IndexError from False引发TypeError,而不是IndexError。 让我的一天。
-
相关:__cause__和__context__有什么区别?
-
不确定这是否适合提及它,但对于使用Spyder的人来说:这整个构造在那里不起作用。 这已经是一个问题超过3年了(github.com/spyder-ide/spyder/issues/2943),但他们似乎认为不需要链式异常。
不同之处在于,当您使用from时,会设置__cause__属性,并且消息指出异常是由直接引起的。如果省略from,则不设置__cause__,但也可以设置__context__属性,然后回溯会在处理其他事件时显示上下文。
如果在异常处理程序中使用raise,则会设置__context__;如果您在其他任何地方使用raise,则也不设置__context__。
如果设置了__cause__,则还会在异常上设置__suppress_context__ = True标志;当__suppress_context__设置为True时,打印回溯时将忽略__context__。
从异常处理程序引发时,您不希望显示上下文(不希望处理期间发生另一个异常消息),然后使用raise ... from None将__suppress_context__设置为True。
换句话说,Python在异常上设置了一个上下文,这样你就可以反省引发异常的位置,让你看看是否有另一个异常被它取代。您还可以向异常添加原因,使回溯显式关于另一个异常(使用不同的措辞),并忽略上下文(但在调试时仍然可以进行内省)。使用raise ... from None可以抑制正在打印的上下文。
请参阅raise声明文档:
The from clause is used for exception chaining: if given, the second expression must be another exception class or instance, which will then be attached to the raised exception as the __cause__ attribute (which is writable). If the raised exception is not handled, both exceptions will be printed:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| >>> try:
... print(1 / 0)
... except Exception as exc:
... raise RuntimeError("Something bad happened") from exc
...
Traceback (most recent call last):
File"<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File"<stdin>", line 4, in <module>
RuntimeError: Something bad happened |
A similar mechanism works implicitly if an exception is raised inside an exception handler or a finally clause: the previous exception is then attached as the new exception’s __context__ attribute:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| >>> try:
... print(1 / 0)
... except:
... raise RuntimeError("Something bad happened")
...
Traceback (most recent call last):
File"<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File"<stdin>", line 4, in <module>
RuntimeError: Something bad happened |
另请参阅内置异常文档,以获取有关上下文的详细信息以及附加到异常的原因信息。
-
是否有任何理由使用from和__cause__代替隐式__context__显式链接异常?有没有人会附加一个不同于except所捕获的异常的情况?
-
@darkfeline:假设您的数据库API支持从各种来源打开数据库,包括Web和磁盘。如果打开数据库失败,您的API将始终引发DatabaseError。但是如果失败是IOError的结果,因为文件无法打开或者HTTPError因为URL无法工作那么这就是你想要显式包含的上下文,所以使用API??的开发人员可以调试为什么这是。那时你使用raise DatabaseError from original_exception。
-
@darkfeline:如果该开发人员在自己的API中包含数据库API的使用,并希望将IOError或HTTPError传递给他们的消费者,那么他们必须使用raise NewException from databaseexception.__cause__,现在使用与他们刚刚捕获的DatabaseException不同的例外。
-
你答案中的__suppress_context__ = True应该只是__suppress_context__,不是吗?因为后面的部分表明如果使用raise ... from None,它只设置为True。
-
@FichteFoll不确定你的意思;您可以将其明确设置为True,但当您使用任何raise ... from ...(None或其他)时,它会自动设置为True。
-
再读一遍后,我明白了。我假设__suppress_context__只有在被提升from None时才会被设置为true,但如果设置了__cause__,则会以不同的方式打印回溯。否则将打印__cause__和__context__。
-
Python 2中的raise .. from构造是否有from __future__?
-
@ dan3:不,没有。异常链接纯粹是Python 3的一项功能。
-
简而言之,每当我想保留原始异常对象时,使用raise from,对吗?
-
@ laike9m:你的意思是当你处理异常foo时,想要引发一个新的异常bar?然后你可以使用raise bar from foo并使Python状态foo直接导致bar。如果你不使用from foo,那么Python仍会打印两者,但是说明在处理foo期间,bar被引发,不同的消息,旨在标记错误处理中可能存在的错误。
-
我的意思是新引发的异常__cause__ attr是原始异常,它可能对将来的错误处理有用,而不使用raise from会抛出原始异常对象。
-
@ laike9m:不,不是在异常处理程序中使用raise时。然后仍然设置__context__。
-
那么它只取决于您需要打印的消息是什么?
-
@ laike9m:完全正确。两者都是调试辅助工具,当然不是最终用户UI。
-
如果代码是为Python 3编写的,并且您希望将其修改为在Python 2和3上都有效,那么最好的方法是什么?我应该用e2 = Error(...); e2.__cause__ = e; raise e2替换raise Error(...) from e(当然,除了在不同的行上)。这与之前的Python 3相同,并且在Python 2.7上工作,对吧?否则我需要为Python 2和3提供单独的文件,并在导入时要小心检查版本...
-
@ArtOfWarfare:是的,您必须创建实例,手动设置__cause__属性,然后引发更新的异常。这适用于2和3.但是,在Python 3中,如果您不删除e2变量,则最终可能会得到循环引用(因为raise设置的__traceback__属性将包含间接引用到框架,从而到e2名称等)。 six项目创建一个使用try...finally的函数。
-
@MartijnPieters - 我们其中一个人误读了six项目中的代码。如果您的Python版本低于3.2,则第731行是else。第733行显示它只是raise value并忽略from_value参数。
-
@MartijnPieters - 也许我最终应该继续从Python 2.7开始...我需要的最终库我正在等待(Fabric)似乎有一个维护得很好的端口现在可用于Python 3.我只会迟到8年对于派对,我总是确保我的代码在两个版本中运行...现在我终于放弃了2.7?
-
@ArtOfWarfare:如果你正在编写多语言代码,那么它将在2.7和3.x上运行,因此在3.x情况下你真的希望finally部分运行;毕竟你刚刚设置了__cause__。