当Python3中已存在异常时引发异常

Raising exceptions when an exception is already present in Python 3

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

1
2
3
4
5
6
7
8
9
10
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运行,我得到:

1
2
3
4
5
6
7
8
9
10
11
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我得到:

1
second

问题

  • 我的第一个例外是什么?
  • 为什么只有最外层的异常是可捕获的?
  • 如何剥离最外层的异常并重新评估早期的异常?
  • 更新0

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


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

    1
    raise B('second') from None

    这将删除异常A回溯。

    1
    2
    3
    4
    Traceback (most recent call last):
      File"raising_more_exceptions.py", line 8, in
        raise B('second')
    __main__.B: second


    "引发"异常在上一个异常处理程序中作为C."上下文"提供。python使用这些信息来呈现更有用的回溯。在python 2.x下,原来的异常会丢失,这只适用于python 3。

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

    1
    2
    3
    4
    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


    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

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


    我相信回答你问题的所有要素都已经在现有答案中了。让我结合并详细阐述。

    让我重复您的问题代码,以提供行号参考:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     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)

    所以要回答你的问题:

  • 我的第一个例外是什么?
  • 您的第一个异常A出现在第6行。第7行中的finally条款总是在try块(第5-6行)离开后立即执行,不管是因为成功完成还是因为引发的异常而离开。在执行finally条款时,第8行又提出了一个例外B。正如伦纳德和伊格纳齐奥所指出的,只有一个例外,即最近被提出的例外,可以被跟踪。因此,一旦B被提出,整个try区块(第4-8行)就退出,如果与之匹配(如果XB的话),第9行的except语句就会捕获异常B

  • 为什么只有最外层的异常是可捕获的?
  • 希望我对1的解释现在已经清楚了。不过,您可以捕获内部/下部/第一个异常。要合并Lennart的回答,稍微修改一下,下面介绍如何同时捕捉这两个问题:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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)

    输出是:

    1
    ('second', A('first',))
  • 如何剥离最外层的异常并重新评估早期的异常?
  • 在Lennart的例子中,这个问题的解决方案是行except A as e,在该行内/下/第一个异常被捕获并存储在变量e中。

    作为一种直觉一般的感觉,什么时候抓住例外,什么时候忽视它们,什么时候再提出,也许这个问题和亚历克斯·马泰利的回答有帮助。


  • 它被扔掉了。
  • 每个线程一次只能有一个异常是"活动的"。
  • 除非您以某种方式将前面的异常封装到后面的异常中,否则您不能这样做。