python 上下文管理器如何将异常重新引发到装饰的生成器中?



这是一个关于contextmanager如何做它所做的事情的问题。

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

@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,因为如果我移除装饰器,只做一个"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代码如何做到这一点。我在这里错过的语言有什么基本的东西?

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

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

这是否使它更清楚?

相关内容

最新更新