繁体   English   中英

在 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循环的主体,有点像带有yield和块的 Ruby 函数。 这不是 Python 中的工作方式。

当执行命中yield ,生成器的堆栈帧被挂起并从堆栈中移除,并且控制返回到(隐式)调用生成器的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相同的错误, try ... except那里!!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM