python如何重新引发已经捕获的异常?

Tia*_*Liu 5 python exception

import sys
def worker(a):
    try:
        return 1 / a
    except ZeroDivisionError:
        return None


def master():
    res = worker(0)
    if not res:
        print(sys.exc_info())
        raise sys.exc_info()[0]
Run Code Online (Sandbox Code Playgroud)

作为上面的代码段,我有一堆函数,比如worker。他们已经有了自己的 try-except 块来处理异常。然后一个主函数将调用每个工人。现在,sys.exc_info() 将所有 None 返回到 3 个元素,如何在 master 函数中重新引发异常?我正在使用 Python 2.7

一个更新:我有 1000 多个工人,有些工人逻辑非常复杂,他们可能同时处理多种类型的异常。所以我的问题是我可以只从 master 提出这些例外而不是编辑作品吗?

Gre*_*Guy 10

在您的情况下,worker返回中的异常None。一旦发生这种情况,就无法恢复异常。如果您的主函数知道每个函数的返回值应该是什么(例如,ZeroDivisionErrorworkerreutrns 中None,您可以手动重新引发异常。

如果您无法自己编辑工作人员功能,我认为您无能为力。您也许可以使用此答案中的一些解决方案,如果它们在代码和控制台上都有效。

上面 krfol 的代码有点像 C 处理异常的方式——有一个全局变量,每当异常发生时,都会分配一个数字,以后可以交叉引用以确定异常是什么。这也是一个可能的解决方案。

但是,如果您愿意编辑工作函数,那么将异常升级到调用该函数的代码实际上非常简单:

try: 
    # some code
except:
    # some response
    raise
Run Code Online (Sandbox Code Playgroud)

如果raisecatch块的末尾使用空格,它会重新引发刚刚捕获的相同异常。或者,如果您需要调试打印,您可以命名异常,并执行相同的操作,甚至引发不同的异常。

except Exception as e:
    # some code
    raise e
Run Code Online (Sandbox Code Playgroud)


aba*_*ert 5

你试图做的事情不会成功。一旦处理了异常(而不重新引发它),异常和伴随的状态就会被清除,因此无法访问它。如果您希望异常保持活动状态,则必须不处理它,或者手动使其保持活动状态。

\n

这在文档中并不容易找到(关于 CPython 的底层实现细节更容易一些,但理想情况下我们想知道 Python 该语言定义了什么),但它就在那里,埋藏在except

\n
\n

\xe2\x80\xa6 这意味着必须为异常分配一个不同的名称,以便能够在 except 子句之后引用它。异常会被清除,因为附加了回溯,它们与堆栈帧形成了一个引用循环,使该帧中的所有局部变量保持活动状态,直到发生下一次垃圾收集。

\n

在执行 except 子句\xe2\x80\x99s 套件之前,有关异常的详细信息存储在模块中sys,可以通过sys.exc_info(). sys.exc_info()返回一个由异常类、异常实例和回溯对象(请参阅标准类型层次结构部分)组成的 3 元组,该对象标识程序中发生异常的点。sys.exc_info()当从处理异常的函数返回时,值将恢复为之前的值(调用之前)。

\n
\n

另外,这确实是异常处理程序的要点:当函数处理异常时,对于该函数外部的世界来说,看起来好像没有发生异常。这在 Python 中比在许多其他语言中更重要,因为 Python 如此混杂地使用异常\xe2\x80\x94每个for循环,每个hasattr调用等都会引发并处理异常,而你不想看到它们。

\n
\n

因此,最简单的方法就是将工作人员更改为不处理异常(或者记录然后重新引发异常,或者其他什么),并让异常处理按其预期方式工作。

\n

在某些情况下您无法执行此操作。例如,如果您的实际代码在后台线程中运行工作程序,则调用者将不会看到异常。在这种情况下,您需要手动将其传回。举一个简单的例子,让我们更改辅助函数的 API 以返回一个值和一个异常:

\n
def worker(a):\n    try:\n        return 1 / a, None\n    except ZeroDivisionError as e:\n        return None, e\n\ndef master():\n    res, e = worker(0)\n    if e:\n        print(e)\n        raise e\n
Run Code Online (Sandbox Code Playgroud)\n

显然,您可以进一步扩展以返回整个exc_info三元组,或者您想要的任何其他内容;我只是让示例尽可能简单。

\n

如果您仔细查看诸如 之类的内容concurrent.futures,就会发现这就是它们处理将线程或进程池上运行的任务的异常传递回父级的方式(例如,当您等待 a 时Future)。

\n
\n

如果你不能修改工人,那你基本上就不走运了。当然,您可以编写一些可怕的代码来在运行时修补工作程序(通过使用inspect获取其源代码,然后使用ast解析、转换和重新编译它,或者直接深入到字节码),但这几乎永远不会对于任何类型的生产代码来说这都是一个好主意。

\n