【问题标题】:How does python contextmanager reraise an exception back into the decorated generator?python contextmanager 如何将异常重新引发回修饰的生成器?
【发布时间】:2021-04-09 06:16:58
【问题描述】:

这是一个关于contextmanager 是如何工作的问题。

contextmanger 是一个装饰器,它调用装饰函数(生成器)两次,以构建__enter____exit__ 函数,供with 子句使用,到目前为止一切顺利。我不明白的是——当with 块内引发异常时,生成器内的except怎么能捕获它? p>

@contextmanager
def f():
    try:
        yield 'foo'
    except Exception as e:
        print('How can I ever reach here??')
        print(e)
    finally:
        print('finally')

with f() as p:
    print(p)
    raise Exception('bar')

输出是

foo
How can I ever reach here??
bar
finally

我认为魔法发生在@contextmanager 中,因为如果我删除装饰器,并且只执行“yield inside try 块”,则生成器外部的异常不会在生成器内部捕获:

def f():
    try:
        yield 'foo'
    except Exception as e:
        print('How can I ever reach here??')
        print(e)
    finally:
        print('finally')

g = f()
print(next(g))
raise Exception('bar')

输出是

foo
Traceback (most recent call last):
...
Exception: bar

我查看了contextlib.contextmanager 代码,但仍然无法弄清楚使用纯 python 代码如何实现这一点。关于我在这里错过的语言的一些基本知识?

【问题讨论】:

    标签: python exception generator yield contextmanager


    【解决方案1】:

    让您感到困惑的逻辑在_GeneratorContextManager 中。你的函数f 是 self.gen。代码刚刚调用了next(self.gen),取回了字符串"foo",正在等待。 f() 位于 yield 语句的中间。

    此时你抛出一个异常。由于 python 看到你在 with 块中,(并且这些是内置在语言中的),它调用生成器的 __exit__ 方法,并带有描述错误的参数。这就是上下文管理器的工作方式。上下文管理器调用self.gen.throw,它通过抛出异常来恢复生成器。进去。瞧。您在异常处理程序中。

    这样会更清楚吗?

    【讨论】:

    • "以type 作为错误类型,value 为无" - 什么?不,值是异常对象,不是None
    • 你是对的。我将它与不同的构造混淆了。但我的流程是正确的。会更正。谢谢。
    • 这很有帮助,谢谢。您的回答将我引向PEP 342,它添加了一个 .throw() 方法。这个 .throw() 可以将异常注入到生成器中,生成器被添加到原生 python 中,而这正是我错过的。
    • 顺便说一句,在你的回答中,函数foo应该是函数f,因为只有函数f和字符串'foo'
    • @QnA。谢谢。固定。
    猜你喜欢
    • 2017-03-02
    • 1970-01-01
    • 2010-12-08
    • 2011-09-12
    • 1970-01-01
    • 2021-10-04
    • 2018-12-14
    • 2023-03-31
    • 2017-03-28
    相关资源
    最近更新 更多