python - "Uncatching"python中的异常

标签 python exception exception-handling

我应该如何“重新抛出”异常,即假设:

  • 我在我的代码中尝试了一些东西,但不幸的是它失败了。
  • 我尝试了一些“聪明”的解决方法,但这次也失败了

如果我从(失败的)解决方法中抛出异常,用户会非常困惑,所以我认为最好重新抛出原始异常 (?),< em>带有描述性回溯(关于实际问题)...

注意:这方面的激励示例是在调用 np.log(np.array(['1'], dtype=object)) 时,它是 tries a witty workaround and gives an AttributeError (它“真的”是一个 TypeError)。

我能想到的一种方法是重新调用有问题的函数,但这似乎是顽固的(一方面,理论上原始函数在第二次调用时可能会表现出一些不同的行为):
好吧,这是一个糟糕的例子,但这里是……

def f():
    raise Exception("sparrow")

def g():
    raise Exception("coconut")

def a():
    f()

假设我这样做了:

try:
    a()
except:
    # attempt witty workaround
    g()
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-4-c76b7509b315> in <module>()
      3 except:
      4     # attempt witty workaround
----> 5     g()
      6

<ipython-input-2-e641f2f9a7dc> in g()
      4
      5 def g():
----> 6     raise Exception("coconut")
      7
      8

Exception: coconut

嗯,问题根本不在于椰子,而在于麻雀:

try:
    a()
except:
    # attempt witty workaround
    try:
        g()
    except:
        # workaround failed, I want to rethrow the exception from calling a()
        a() # ideally don't want to call a() again
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-4-e641f2f9a7dc> in <module>()
     19     except:
     20         # workaround failed, I want to rethrow the exception from calling a()
---> 21         a()  # ideally don't want to call a() again

<ipython-input-3-e641f2f9a7dc> in a()
      8
      9 def a():
---> 10     f()
     11
     12

<ipython-input-1-e641f2f9a7dc> in f()
      1 def f():
----> 2     raise Exception("sparrow")
      3
      4
      5 def g():

Exception: sparrow

有没有标准的方法来处理这个问题,还是我认为它完全错误?

最佳答案

如果您想让最终用户觉得您从未调用过 g(),那么您需要存储第一个错误的回溯,调用第二个函数,然后抛出原始错误与原始回溯。 (否则,在 Python2 中,bare raise 会重新引发第二个异常而不是第一个)。问题是没有 2/3 兼容的方式来引发回溯,因此您必须将 Python 2 版本包装在 exec 语句中(因为它是一个 SyntaxError 在Python 3)。

这是一个可以让你做到这一点的函数(我最近将它添加到 pandas 代码库中):

import sys
if sys.version_info[0] >= 3:
    def raise_with_traceback(exc, traceback=Ellipsis):
        if traceback == Ellipsis:
            _, _, traceback = sys.exc_info()
        raise exc.with_traceback(traceback)
else:
    # this version of raise is a syntax error in Python 3
    exec("""
def raise_with_traceback(exc, traceback=Ellipsis):
    if traceback == Ellipsis:
        _, _, traceback = sys.exc_info()
    raise exc, None, traceback
""")

raise_with_traceback.__doc__ = (
"""Raise exception with existing traceback.
If traceback is not passed, uses sys.exc_info() to get traceback."""
)

然后你可以像这样使用它(为了清楚起见,我还更改了异常类型)。

def f():
    raise TypeError("sparrow")

def g():
    raise ValueError("coconut")

def a():
    f()

try:
    a()
except TypeError as e:
    import sys
    # save the traceback from the original exception
    _, _, tb = sys.exc_info()
    try:
        # attempt witty workaround
        g()
    except:
        raise_with_traceback(e, tb)

而在 Python 2 中,您只能看到 a()f():

Traceback (most recent call last):
  File "test.py", line 40, in <module>
    raise_with_traceback(e, tb)
  File "test.py", line 31, in <module>
    a()
  File "test.py", line 28, in a
    f()
  File "test.py", line 22, in f
    raise TypeError("sparrow")
TypeError: sparrow

但在 Python 3 中,它仍然注意到还有一个额外的异常,因为您在其 except 子句中引发 [这会颠倒错误的顺序并使用户更加困惑]:

Traceback (most recent call last):
  File "test.py", line 38, in <module>
    g()
  File "test.py", line 25, in g
    raise ValueError("coconut")
ValueError: coconut

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test.py", line 40, in <module>
    raise_with_traceback(e, tb)
  File "test.py", line 6, in raise_with_traceback
    raise exc.with_traceback(traceback)
  File "test.py", line 31, in <module>
    a()
  File "test.py", line 28, in a
    f()
  File "test.py", line 22, in f
    raise TypeError("sparrow")
TypeError: sparrow

如果你绝对希望它看起来像 g() 异常在 Python 2 和 Python 3 中都没有发生,你需要检查你是否在 except 子句第一:

try:
    a()
except TypeError as e:
    import sys
    # save the traceback from the original exception
    _, _, tb = sys.exc_info()
    handled = False
    try:
        # attempt witty workaround
        g()
        handled = True
    except:
        pass
    if not handled:
        raise_with_traceback(e, tb)

在 Python 2 中为您提供以下回溯:

Traceback (most recent call last):
  File "test.py", line 56, in <module>
    raise_with_traceback(e, tb)
  File "test.py", line 43, in <module>
    a()
  File "test.py", line 28, in a
    f()
  File "test.py", line 22, in f
    raise TypeError("sparrow")
TypeError: sparrow

Python 3 中的这个追溯:

Traceback (most recent call last):
  File "test.py", line 56, in <module>
    raise_with_traceback(e, tb)
  File "test.py", line 6, in raise_with_traceback
    raise exc.with_traceback(traceback)
  File "test.py", line 43, in <module>
    a()
  File "test.py", line 28, in a
    f()
  File "test.py", line 22, in f
    raise TypeError("sparrow")
TypeError: sparrow

它确实添加了一个额外的无用的回溯行,向用户显示 raise exc.with_traceback(traceback),但它相对干净。

关于python - "Uncatching"python中的异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17001473/

相关文章:

python - 在我的异常中停止,而不是库代码

python - 如何在 django View 中显示带有超链接的树结构

python - CNTK python 中的用户定义层

Python 3 异常 : TypeError: function missing 1 required positional argument: 'words'

C++ catch block - 通过值或引用捕获异常?

c# - 捕获在不同线程中抛出的异常

python - 如何使用 Azure Blob 存储挂载数据?

python - 在 Ubuntu 中启动时运行 Python 脚本

java - 使用 fragment 在 Android 构造函数中设置 View

javascript - 无法在 Javascript async/await try/catch 客户端代码中访问 res.statusMessage 来读取 Express 后端抛出的异常