在 Python 中捕获生成器调用者抛出的异常
Catch exception thrown in generator caller in Python
我正在尝试捕获生成器调用者抛出的异常:
class MyException(Exception):
pass
def gen():
for i in range(3):
try:
yield i
except MyException:
print 'handled exception'
for i in gen():
print i
raise MyException
这输出
$ python x.py
0
Traceback (most recent call last):
File "x.py", line 14, in <module>
raise MyException
__main__.MyException
当我打算让它输出时
$ python x.py
0
handled exception
1
handled exception
2
handled exception
回想起来,我认为这是因为调用者与生成器有不同的堆栈,所以异常没有冒泡到生成器。 对吗?有没有其他方法可以捕获调用者引发的异常?
旁白: 我可以使用 generator.throw() 使其工作,但这需要修改调用者:
def gen():
for i in range(3):
try:
yield i
except MyException:
print 'handled exception'
yield
import sys
g = gen()
for i in g:
try:
print i
raise MyException
except:
g.throw(*sys.exc_info())
您可能会想,当生成器中的执行命中 yield
时,生成器会执行 for
循环的主体,有点像带有 [=10 的 Ruby 函数=] 和一个块。这不是 Python.
中的工作方式
当执行命中 yield
时,生成器的堆栈帧被挂起并从堆栈中移除,并且控制 returns 到(隐式)调用生成器的 next
方法的代码。然后该代码进入循环体。引发异常时,生成器的堆栈帧不在堆栈上,并且异常在冒泡时不会通过生成器。
生成器无法响应此异常。
您可能还被上下文管理器 (contextlib.contextmanager
) 中的 yield
弄糊涂了 - 正如我刚刚到达这里一样。
它们的用法可以是:
from contextlib import contextmanager
@contextmanager
def mycontext():
try:
yield
except MyException:
print 'handled exception'
因此,对于与您上述情况类似的情况,我的解决方案是:
def gen():
for i in range(3):
yield i
for ii in gen():
with mycontext():
print ii
raise MyException
它给出了 预期的 输出并使用了所有产量。
晚会有点晚了,但也许有类似想法的人会觉得它有帮助。
注意:将上下文放在生成器内部与在那里使用 try ... except
的错误相同!!
我正在尝试捕获生成器调用者抛出的异常:
class MyException(Exception):
pass
def gen():
for i in range(3):
try:
yield i
except MyException:
print 'handled exception'
for i in gen():
print i
raise MyException
这输出
$ python x.py
0
Traceback (most recent call last):
File "x.py", line 14, in <module>
raise MyException
__main__.MyException
当我打算让它输出时
$ python x.py
0
handled exception
1
handled exception
2
handled exception
回想起来,我认为这是因为调用者与生成器有不同的堆栈,所以异常没有冒泡到生成器。 对吗?有没有其他方法可以捕获调用者引发的异常?
旁白: 我可以使用 generator.throw() 使其工作,但这需要修改调用者:
def gen():
for i in range(3):
try:
yield i
except MyException:
print 'handled exception'
yield
import sys
g = gen()
for i in g:
try:
print i
raise MyException
except:
g.throw(*sys.exc_info())
您可能会想,当生成器中的执行命中 yield
时,生成器会执行 for
循环的主体,有点像带有 [=10 的 Ruby 函数=] 和一个块。这不是 Python.
当执行命中 yield
时,生成器的堆栈帧被挂起并从堆栈中移除,并且控制 returns 到(隐式)调用生成器的 next
方法的代码。然后该代码进入循环体。引发异常时,生成器的堆栈帧不在堆栈上,并且异常在冒泡时不会通过生成器。
生成器无法响应此异常。
您可能还被上下文管理器 (contextlib.contextmanager
) 中的 yield
弄糊涂了 - 正如我刚刚到达这里一样。
它们的用法可以是:
from contextlib import contextmanager
@contextmanager
def mycontext():
try:
yield
except MyException:
print 'handled exception'
因此,对于与您上述情况类似的情况,我的解决方案是:
def gen():
for i in range(3):
yield i
for ii in gen():
with mycontext():
print ii
raise MyException
它给出了 预期的 输出并使用了所有产量。
晚会有点晚了,但也许有类似想法的人会觉得它有帮助。
注意:将上下文放在生成器内部与在那里使用 try ... except
的错误相同!!