【问题标题】:Raising exceptions when an exception is already present in Python 3当 Python 3 中已经存在异常时引发异常
【发布时间】:2011-09-10 19:53:00
【问题描述】:

在以下代码中引发第二个异常 (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(最近一次调用最后一次):
  文件“raising_more_exceptions.py”,第 6 行,在
    提高A('第一')
__main__.A:首先

在处理上述异常的过程中,又出现了一个异常:

回溯(最近一次通话最后):
  文件“raising_more_exceptions.py”,第 8 行,在
    提高 B('第二')
__main__.B:第二个

但如果X = B 我得到:

问题

  1. 我的第一个异常去哪儿了?
  2. 为什么只能捕获最外层的异常?
  3. 如何剥离最外层的异常并重新引发早期的异常?

更新0

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

【问题讨论】:

  • 答案似乎忽略了这样一个事实,即当没有捕获到异常时,我仍然可以获得完整的回溯。请解释一下?

标签: python exception exception-handling python-3.x


【解决方案1】:

回答问题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

【讨论】:

  • 谢谢,这解决了我的问题...想从基类方法中的 KeyError 引发自定义异常。 My List 是一个子类属性,子类对象是动态生成的。默认的 KeyError trackback 没有说明错误发生在哪个子类中,所以我想抑制它并使用更多信息引发一个新异常。 “从无中提升 X”允许我这样做。
  • 如果你想保存原始回溯,你可以使用:raise OtherException(...).with_traceback(tb) 如文档docs.python.org/3/library/exceptions.html#BaseException中所述
  • 这不是问题 3 所问的。问题 3 询问如何移除 B 并重新加注 A,而不是如何只加注 B 并压制 A。
【解决方案2】:

“导致”异常在您的最后一个异常处理程序中以 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

【讨论】:

    【解决方案3】:

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

    在您的情况下,当在“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
    

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

    【讨论】:

    • @Matt:它不会去任何地方。不过,我确实意识到我脑子有问题并更新了我的答案。
    • 如何在 doctests 中处理这种情况?
    • 或者可以使用raise x from y
    • 如果你只支持 Python 3,可以。现在这是合理的,但不是在 2011 年。
    【解决方案4】:

    我相信回答您问题的所有要素都已包含在现有答案中。让我结合并详细说明。

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

     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. 我的第一个异常去哪儿了?

    您的第一个异常 A 在第 6 行引发。第 7 行中的 finally 子句总是try 块(第 5-6 行)离开后立即执行,无论是因为成功完成还是因为引发异常而留下。 在执行 finally 子句时,第 8 行引发了另一个异常 B。正如 Lennart 和 Ignazio 所指出的,只有一个例外,即最近提出的例外,可以被跟踪。因此,一旦引发B,整个try 块(第4-8 行)就会退出,如果匹配,则第9 行中的except 语句将捕获异常B(如果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)
    

    输出是:

    ('second', A('first',))
    
    1. 如何剥离最外层的异常并重新引发早期的异常?

    在 Lennart 的示例中,该问题的解决方案是行 except A as e,其中捕获了内部/下部/第一个异常并将其存储在变量 e 中。

    对于何时捕获异常、何时忽略它们以及何时重新引发异常的一般直觉,也许是this question and Alex Martelli's answer 帮助。

    【讨论】:

      【解决方案5】:
      1. 它被扔掉了。
      2. 每个线程一次只能“激活”一个异常。
      3. 你不能,除非你以某种方式将前面的异常封装在后面的异常中。

      【讨论】:

      • 如果它被抛出,为什么我在没有捕获到异常的情况下得到完整的回溯?
      • 因为你用来运行脚本的工具已经安装了一个全局异常处理程序,并且它注意到了双重异常。
      • 我现在明白了,Python 3.x 有不同的行为。是解释器自己在捕捉它。在 2.x 中,第一个异常被静默删除。
      猜你喜欢
      • 2012-02-11
      • 2018-12-14
      • 1970-01-01
      • 2019-07-15
      • 2019-03-02
      • 1970-01-01
      • 1970-01-01
      • 2016-03-18
      • 2010-11-24
      相关资源
      最近更新 更多