如何在嵌套的try/except块中重新引发异常?

Tob*_*ler 80 python nested exception raise

我知道如果我想重新引发一个异常,我raise在相应的except块中使用不带参数.但是给出了一个嵌套的表达式

try:
    something()
except SomeError as e:
    try:
        plan_B()
    except AlsoFailsError:
        raise e  # I'd like to raise the SomeError as if plan_B()
                 # didn't raise the AlsoFailsError
Run Code Online (Sandbox Code Playgroud)

如何在SomeError不破坏堆栈跟踪的情况下重新提升?raise在这种情况下,单独会重新提高最近AlsoFailsError.或者我怎么能重构我的代码以避免这个问题?

use*_*342 93

您可以将异常类型,值和回溯存储在局部变量中,并使用以下三参数形式raise:

try:
    something()
except SomeError:
    t, v, tb = sys.exc_info()
    try:
        plan_B()
    except AlsoFailsError:
        raise t, v, tb
Run Code Online (Sandbox Code Playgroud)

在Python 3中,回溯存储在异常中,因此raise e将执行(大多数)正确的操作:

try:
    something()
except SomeError as e:
    try:
        plan_B()
    except AlsoFailsError:
        raise e
Run Code Online (Sandbox Code Playgroud)

上面唯一的问题是它会产生一个稍微误导性的回溯,告诉你SomeError在处理过程中发生的事情AlsoFailsError(因为raise e内部except AlsoFailsError),实际上几乎完全相反的情况发生了 - 我们AlsoFailsError在尝试恢复时处理SomeError.要禁用此行为并获取从未提及的回溯AlsoFailsError,请替换raise eraise e from None.

  • @TobiasKienzler` raise t,None,tb`将丢失异常的值,并强制`raise`从类型中重新实例化它,给你一个不太具体(或者根本就不正确)的异常值.例如,如果引发的异常是`KeyError("some-key")`,它将重新引发`KeyError()`并省略traceback中的确切缺失键. (3认同)
  • @TobiasKienzler它仍然可以在Python 3中表达为"raise v.with_traceback(tb)`".(你的评论甚至说了很多,除了它建议重新实例化这个价值.) (3认同)
  • 另外,在Python 2.0(13年前发布)之前,没有将`sys.exc_info()`存储在局部变量中的红色警告是有道理的,但今天接近荒谬.没有循环收集器,现代Python几乎没用,因为每个非平凡的Python库都会创建没有暂停的循环,并依赖于它们的正确清理. (2认同)
  • @user4815162342 您可以通过编写“raise e from None”来终止“发生另一个错误”嵌套错误。 (2认同)

Lau*_*RTE 16

即使接受的解决方案是正确的,也可以指向具有Python 2 + 3解决方案的Sixsix.reraise.

六.重新加载(exc_type,exc_value,exc_traceback = None)

重新引发异常,可能使用不同的回溯.[...]

所以,你可以写:

import six


try:
    something()
except SomeError:
    t, v, tb = sys.exc_info()
    try:
        plan_B()
    except AlsoFailsError:
        six.reraise(t, v, tb)
Run Code Online (Sandbox Code Playgroud)


Tob*_*ler 9

根据Drew McGowen的建议,但是处理一般情况(存在返回值s),这里是user4815162342答案的替代方案:

try:
    s = something()
except SomeError as e:
    def wrapped_plan_B():
        try:
            return False, plan_B()
        except:
            return True, None
    failed, s = wrapped_plan_B()
    if failed:
        raise
Run Code Online (Sandbox Code Playgroud)

  • @ user4815162342好点:)虽然同时在Python3中我会考虑`from from`,所以堆栈跟踪也会让我计划B失败.顺便说一下[可以在Python 2中模拟](http://stackoverflow.com/a/29833994/321973). (2认同)

Mat*_*chs 7

Python 3.5+ 无论如何都会将回溯信息附加到错误中,因此不再需要单独保存它。

>>> def f():
...   try:
...     raise SyntaxError
...   except Exception as e:
...     err = e
...     try:
...       raise AttributeError
...     except Exception as e1:
...       raise err from None
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 9, in f
  File "<stdin>", line 3, in f
SyntaxError: None
>>> 
Run Code Online (Sandbox Code Playgroud)

  • @TobiasKienzler 3.5+(我将其更改为)似乎是一种全球认可的格式。denkst du 吗?;) (3认同)
  • 问题是关于在 `except` 期间发生的 _another_ 异常。但是你是对的,当我将 `err = e` 替换为 `raise AttributeError` 时,你首先得到的是 `SyntaxError` 堆栈跟踪,然后是 `在处理上述异常时,发生了另一个异常:` 和`AttributeError` 堆栈跟踪。很高兴知道,但不幸的是,不能依赖 3.5+ 的安装。PS: ff verstehen nicht-Deutsche vermutlich nicht ;) (2认同)