繁体   English   中英

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

[英]python how to re-raise an exception which is already caught?

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]

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

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

未经测试,但我怀疑您可以做这样的事情。 根据变量的范围,您必须对其进行更改,但我想您会明白的

try:
    something
except Exception as e:
    variable_to_make_exception = e

.....稍后使用变量

使用这种方式处理错误的示例:

errors = {}
try:
    print(foo)
except Exception as e:
    errors['foo'] = e
try:
    print(bar)
except Exception as e:
    errors['bar'] = e


print(errors)
raise errors['foo']

输出..

{'foo': NameError("name 'foo' is not defined",), 'bar': NameError("name 'bar' is not defined",)}
Traceback (most recent call last):
  File "<input>", line 13, in <module>
  File "<input>", line 3, in <module>
NameError: name 'foo' is not defined

您尝试执行的操作无效。 一旦处理了异常(不重新引发),该异常及其伴随的状态便被清除,因此无法访问它。 如果要让异常有效,则必须不处理它,或手动使其保持活动状态。

这在文档中并不是那么容易找到(关于CPython的底层实现细节要容易一些,但是理想情况下我们想知道该语言定义的Python),但是它存在于except引用except中:

…这意味着必须将异常分配给其他名称,以便能够在except子句之后引用该异常。 清除异常是因为它们具有附加的回溯,它们与堆栈框架形成了一个参考循环,使该框架中的所有本地对象都保持活动状态,直到发生下一个垃圾回收为止。

在执行except子句的套件之前,有关异常的详细信息存储在sys模块中,并且可以通过sys.exc_info()进行访问。 sys.exc_info()返回一个sys.exc_info()组,该sys.exc_info()组由异常类,异常实例和回溯对象(请参阅标准类型层次结构)组成,用于标识程序中发生异常的点。 从处理异常的函数返回时, sys.exc_info()值将还原为先前的值(在调用之前)。

同样,这确实是异常处理程序的重点:当一个函数处理异常时,对于该函数外部的世界,似乎没有异常发生。 这在Python中比在许多其他语言中更为重要,因为Python如此混杂地使用异常-每个for循环,每个hasattr调用等都引发和处理异常,并且您不希望看到它们。


因此,执行此操作的最简单方法是仅更改工作程序以不处理异常(或先记录日志然后重新引发它们,或执行其他操作),然后让异常处理按预期的方式工作。

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

def worker(a):
    try:
        return 1 / a, None
    except ZeroDivisionError as e:
        return None, e

def master():
    res, e = worker(0)
    if e:
        print(e)
        raise e

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

如果您查看concurrent.futures类之类的东西,这就是它们如何处理将线程或进程池上运行的任务中的异常传递回父级的方式(例如,当您等待Future )。


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

在您的情况下, worker的异常返回None 一旦发生这种情况,就不会再有异常了。 如果你的主函数知道返回值应该是什么每个功能(例如, ZeroDivisionErrorworker reutrns None ,你可以手动再加注例外。

如果您不能自己编辑辅助函数,那么我认为您无能为力。 如果它们可以在代码中以及在控制台上工作,则您可能可以使用此答案中的某些解决方案

上面的krflol代码类似于C处理异常的方式-有一个全局变量,每当发生异常时,就会分配一个数字,该数字以后可以交叉引用以找出异常是什么。 这也是可能的解决方案。

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

try: 
    # some code
except:
    # some response
    raise

如果在catch块的末尾使用空白raise ,则会重新引发刚捕获的异常。 另外,如果您需要调试打印,执行相同的操作,甚至引发其他异常,则可以命名该异常。

except Exception as e:
    # some code
    raise e

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM