python contextmanager 如何将异常重新抛回到修饰生成器中?
How does python contextmanager reraise an exception back into the decorated generator?
这是一个关于 contextmanager
如何做的问题。
contextmanger
是一个装饰器,它调用装饰函数(生成器)两次,以构建 __enter__
和 __exit__
函数,供 [=19 使用=] 子句,到目前为止一切顺利。我不明白的是——当在 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
,因为如果我删除装饰器,只做一个 'yield inside try block',生成器外部的异常不会在生成器内部捕获:
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
通过抛出该异常来恢复生成器。进去。瞧。您在异常处理程序中。
这样是不是更清楚了?
这是一个关于 contextmanager
如何做的问题。
contextmanger
是一个装饰器,它调用装饰函数(生成器)两次,以构建 __enter__
和 __exit__
函数,供 [=19 使用=] 子句,到目前为止一切顺利。我不明白的是——当在 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
,因为如果我删除装饰器,只做一个 'yield inside try block',生成器外部的异常不会在生成器内部捕获:
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
通过抛出该异常来恢复生成器。进去。瞧。您在异常处理程序中。
这样是不是更清楚了?