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

时间:2011-06-08 12:06:36

标签: python exception exception-handling 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)

如果使用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. 如何剥离最外层的异常并重新引用先前的异常?
  4. Update0

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

5 个答案:

答案 0 :(得分:13)

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

QueryParser

将删除异常raise B('second') from None 追溯。

A

答案 1 :(得分:9)

'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

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

答案 2 :(得分: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

如您所见,您现在可以访问原始例外。

答案 3 :(得分: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)

所以回答你的问题:

  1. 我的第一个例外在哪里?
  2. 第6行引发了您的第一个异常A。第{7}行中的finally子句始终执行try块(第5行) -6)是留下的,无论是因为成功完成还是因为引发异常而离开了。 在执行finally子句时,第8行引发了另一个异常B。正如Lennart和Ignazio指出的那样,只有一个例外,即最近被提出的例外,可以被追踪。因此,只要引发B,就会退出整个try块(第4-8行),并且第9行中的B语句会捕获异常except如果匹配(如果XB)。

    1. 为什么只有最外层的例外可以捕获?
    2. 希望现在从我对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)
      

      输出结果为:

      ('second', A('first',))
      
      1. 如何剥离最外层的异常并重新引用先前的异常?
      2. 在Lennart的例子中,这个问题的解决方案是行except A as e,其中内部/下部/第一个异常被捕获并存储在变量e中。

        作为对何时捕捉异常,何时忽略它们以及何时重新加注的一般直觉,可能this question and Alex Martelli's answer帮助。

答案 4 :(得分:6)

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