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

时间:2018-07-05 18:27:57

标签: 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]

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

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

3 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

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

这在文档中并不容易找到(有关CPython的基础实现细节要容易一些,但理想情况下我们想知道该语言定义的Python),但是它已经藏在{{3} }参考:

  

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

     

在执行except子句的套件之前,有关异常的详细信息存储在sys模块中,并且可以通过sys.exc_info()访问。 sys.exc_info()返回一个由异常类,异常实例和回溯对象(请参阅标准类型层次结构)组成的3元组,该对象标识程序中发生异常的点。从处理异常的函数返回时,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之类的事物,这就是它们如何处理将线程或进程池上运行的任务中的异常传递回父级的方式(例如,当您等待{{1} }。


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

答案 2 :(得分:0)

对于您而言,worker中的异常返回None。一旦发生这种情况,就不会再有异常了。如果您的主函数知道每个函数的返回值(例如,ZeroDivisionError中的worker返回None,则可以手动引发异常。

如果您不能自己编辑worker函数,那么我认为您无能为力。如果some of the solutions from this answer既可以在代码中也可以在控制台上工作,则可以使用它们。

上面的

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

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

try: 
    # some code
except:
    # some response
    raise

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

except Exception as e:
    # some code
    raise e