在Python 3中已经存在异常时引发异常

Mat*_*ner 38 python exception-handling exception python-3.x

在下面的代码中引发A第二个(B)时,我的第一个异常()会发生什么?

class A(Exception): pass
class B(Exception): pass

try:
    try:
        raise A('first')
    finally:
        raise B('second')
except X as c:
    print(c)
Run Code Online (Sandbox Code Playgroud)

如果运行X = A我得到:

Traceback (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我得到:

second

问题

  1. 我的第一个例外去了哪里?
  2. 为什么只有最外层的例外可以捕获?
  3. 如何剥离最外层的异常并重新引用先前的异常?

Update0

这个问题专门针对Python 3,因为它的异常处理与Python 2完全不同.

Fli*_*pym 21

回答问题3,您可以使用:

raise B('second') from None
Run Code Online (Sandbox Code Playgroud)

这将删除异常A回溯.

Traceback (most recent call last):
  File "raising_more_exceptions.py", line 8, in 
    raise B('second')
__main__.B: second
Run Code Online (Sandbox Code Playgroud)


Dan*_*iel 10

'cause'异常在上一个异常处理程序中以c .__ context__的形式提供.Python正在使用此信息来呈现更有用的回溯.在Python 2.x下,原始异常将丢失,这仅适用于Python 3.

通常你会使用它来抛出一致的异常,同时仍然保持原始异常可访问(尽管从异常处理程序自动发生它非常酷,我不知道!):

try:
    do_something_involving_http()
except (URLError, socket.timeout) as ex:
    raise MyError('Network error') from ex
Run Code Online (Sandbox Code Playgroud)

这里有更多信息(以及其他一些非常有用的东西):http://docs.python.org/3.3/library/exceptions.html


Len*_*bro 8

Pythons异常处理一次只处理一个异常.但是,异常对象与其他所有对象一样受到相同的变量规则和垃圾收集的约束.因此,如果将异常对象保存在某个变量中,您可以稍后处理它,即使引发了另一个异常.

在您的情况下,当在"finally"语句中引发异常时,Python 3将在第二个异常之前打印出第一个异常的回溯,以便更有帮助.

更常见的情况是您希望在显式异常处理期间引发异常.然后,您可以在下一个异常中"保存"异常.只需将其作为参数传递:

>>> 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
Run Code Online (Sandbox Code Playgroud)

如您所见,您现在可以访问原始异常.

  • 或者可以使用`raise x from y`? (2认同)

cfi*_*cfi 7

我相信回答你问题的所有要素都已经存在于现有的答案中.让我结合并详细说明.

让我重复一下你问题的代码,提供行号参考:

 1  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)
Run Code Online (Sandbox Code Playgroud)

那么回答你的问题:

  1. 我的第一个例外去了哪里?

A6行引发了第一个异常.finally第7 行中的子句总是try块(第5-6行)被保留后立即执行,无论是否由于成功完成或由于引发异常而离开.在finally执行该条款时,第8行引发了另一个例外B.正如Lennart和Ignazio指出的那样,只有一个例外,即最近被提出的例外,可以被追踪.因此,一旦B引发,整个try块(第4-8行)就会退出,如果匹配(如果是),则第9行中Bexcept语句将捕获异常.XB

  1. 为什么只有最外层的例外可以捕获?

希望现在从我对1的解释中可以清楚地看到这一点.但是你可以捕获内/低/第一个异常.要合并Lennart的答案,略微修改,以下是如何捕捉两者:

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)
Run Code Online (Sandbox Code Playgroud)

输出是:

('second', A('first',))
Run Code Online (Sandbox Code Playgroud)
  1. 如何剥离最外层的异常并重新引用先前的异常?

在Lennart的例子中,这个问题的解决方案except A as e是捕获内部/下部/第一个异常并存储在变量中的行e.

作为对何时捕捉异常,何时忽略它们以及何时重新加注的一般直觉,也许这个问题和Alex Martelli的答案有所帮助.


Ign*_*ams 6

  1. 它被抛弃了.
  2. 每个线程一次只能有一个"活动"异常.
  3. 你不能,除非你以某种方式将先前的异常封装在后面的异常中.

  • 我现在看到,Python 3.x有不同的行为.它是解释器本身捕捉它.在2.x中,第一个例外是静默删除. (2认同)