当第二个(B在下面的代码中引发时,我的第一个异常(A发生了什么?
1
2
3
4
5
6
7
8
9
10class A(Exception): pass
class B(Exception): pass
try:
try:
raise A('first')
finally:
raise B('second')
except X as c:
print(c)
如果用X = A运行,我得到:
1
2
3
4
5
6
7
8
9
10
11Traceback (most recent call last):
File"raising_more_exceptions.py", line 6, in
raise A('first')
__main__.A: first
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File"raising_more_exceptions.py", line 8, in
raise B('second')
__main__.B: second
但如果X = B我得到:
1second
问题
我的第一个例外是什么?
为什么只有最外层的异常是可捕获的?
如何剥离最外层的异常并重新评估早期的异常?
更新0
这个问题专门针对python 3,因为它的异常处理与python 2非常不同。
答案似乎忽略了这样一个事实,即当异常没有被捕获时,我仍然可以得到完整的回溯。请解释一下?
回答问题3,您可以使用:
1raise B('second') from None
这将删除异常A回溯。
1
2
3
4Traceback (most recent call last):
File"raising_more_exceptions.py", line 8, in
raise B('second')
__main__.B: second
谢谢,这解决了我的问题…希望从基类方法中的keyError引发自定义异常。我的列表是一个子类属性,子类对象是动态生成的。默认的keyError跟踪没有说明错误发生在哪个子类中,因此我想抑制它并用更多信息引发新的异常。"把x从"无"中提高"允许我这样做。
如果要保存原始的回溯,可以使用:引发OtherException(…).with_traceback(tb),如docs.python.org/3/library/exceptions.html baseexception中所述。
"引发"异常在上一个异常处理程序中作为C."上下文"提供。python使用这些信息来呈现更有用的回溯。在python 2.x下,原来的异常会丢失,这只适用于python 3。
通常,在保持原始异常可访问的同时,您可以使用它来抛出一个一致的异常(尽管从异常处理程序自动发生非常酷,但我不知道!):
1
2
3
4try:
do_something_involving_http()
except (URLError, socket.timeout) as ex:
raise MyError('Network error') from ex
更多信息(以及您可以做的其他一些非常有用的事情):http://docs.python.org/3.3/library/exceptions.html
pythons异常处理一次只能处理一个异常。但是,异常对象和其他对象一样受相同的变量规则和垃圾收集的约束。因此,如果将异常对象保存在某个变量中,即使引发了另一个异常,也可以在以后处理它。
在您的例子中,当在"finally"语句中引发异常时,python 3将在第二个异常之前打印出第一个异常的回溯,这样更有用。
更常见的情况是,您希望在显式异常处理期间引发异常。然后您可以在下一个异常中"保存"该异常。只需将其作为参数传入:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15>>> class A(Exception):
... pass
...
>>> class B(Exception):
... pass
...
>>> try:
... try:
... raise A('first')
... except A as e:
... raise B('second', e)
... except Exception as c:
... print(c.args[1])
...
first
如您所见,您现在可以访问原始异常。
你能回答第一部分吗?
@马特:哪儿也去不了。不过,我确实意识到我有一个脑残,并更新了我的答案。
在教义中如何处理这种情况?
或者可以使用raise x from y?
如果您只支持python 3,是的。这现在是合理的,但在2011年是不合理的。
我相信回答你问题的所有要素都已经在现有答案中了。让我结合并详细阐述。
让我重复您的问题代码,以提供行号参考:
1
2
3
4
5
6
7
8
9
101 class A(Exception): pass
2 class B(Exception): pass
3
4 try:
5 try:
6 raise A('first')
7 finally:
8 raise B('second')
9 except X as c:
10 print(c)
所以要回答你的问题:
我的第一个例外是什么?
您的第一个异常A出现在第6行。第7行中的finally条款总是在try块(第5-6行)离开后立即执行,不管是因为成功完成还是因为引发的异常而离开。在执行finally条款时,第8行又提出了一个例外B。正如伦纳德和伊格纳齐奥所指出的,只有一个例外,即最近被提出的例外,可以被跟踪。因此,一旦B被提出,整个try区块(第4-8行)就退出,如果与之匹配(如果X是B的话),第9行的except语句就会捕获异常B。
为什么只有最外层的异常是可捕获的?
希望我对1的解释现在已经清楚了。不过,您可以捕获内部/下部/第一个异常。要合并Lennart的回答,稍微修改一下,下面介绍如何同时捕捉这两个问题:
1
2
3
4
5
6
7
8
9class A(Exception): pass
class B(Exception): pass
try:
try:
raise A('first')
except A as e:
raise B('second', e)
except Exception as c:
print(c)
输出是:
1('second', A('first',))
如何剥离最外层的异常并重新评估早期的异常?
在Lennart的例子中,这个问题的解决方案是行except A as e,在该行内/下/第一个异常被捕获并存储在变量e中。
作为一种直觉一般的感觉,什么时候抓住例外,什么时候忽视它们,什么时候再提出,也许这个问题和亚历克斯·马泰利的回答有帮助。
它被扔掉了。
每个线程一次只能有一个异常是"活动的"。
除非您以某种方式将前面的异常封装到后面的异常中,否则您不能这样做。
如果它被抛出了,为什么在没有捕获异常的情况下我会得到完整的回溯呢?
因为您用来运行脚本的工具安装了一个全局异常处理程序,并且它注意到了双重异常。
什么工具…?
我现在看到了,python 3.x有不同的行为。正是口译员自己抓住了它。在2.x中,第一个异常被静默删除。