首页 文章

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

提问于
浏览
29

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

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

问题

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

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

  • 如何剥离最外层的异常并重新加载先前的异常?

Update0

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

5 回答

  • 6

    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
    

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

  • 8

    '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

  • 6
    • 它被抛出了 .

    • 每个线程一次只能有一个例外"active" .

    • 你不能,除非你以某种方式将先前的异常封装在后面的异常中 .

  • 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)
    

    那么回答你的问题:

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

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

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

    希望现在从我对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',))
    
    • 如何剥离最外层的异常并重新加载先前的异常?

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

    作为对何时捕捉异常,什么时候忽略它们以及何时重新加注的一般直觉,也许this question and Alex Martelli's answer帮助 .

  • 5

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

    raise B('second') from None
    

    这将删除异常 A traceback .

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

相关问题