为什么当迭代它的循环被中断时,生成器会引发异常?

问题描述 投票:0回答:1
def gen():
    try:
        yield 1
        yield 2
    except:
        print('hi')
def func():
    for x in gen():
        return x
print(func())

此代码打印

hi
,然后打印
1
。为什么它不直接打印
1
?引发了什么异常?

python exception generator
1个回答
6
投票

您的

func
从生成器
gen
获取第一个元素。 之后,
func
有一个
return
声明。 结果,
func
执行结束,生成器
gen
也退出。 当调用生成器的
close
方法时,它会引发
GeneratorExit
异常。 这就是捕获到的异常。 这就是为什么它会打印
hi
。 之后,程序继续;
func
返回
1
并打印在代码的最后一行。

由于生成器死亡,所以会调用

generator.close
方法。 在
for
循环之后,不再有对生成器对象的剩余引用。 如果将生成器分配给变量
a = gen()
并且没有完全消耗其迭代,则可以通过直接调用
GeneratorExit
或杀死对象
a.close()
来引发
del a

另一方面,如果你让你的发电机完成,它不会升高

GeneratorExit
。 当循环
for _ in gen(): pass
完成时,它会完成
gen
生成器的执行,从而产生由
StopIteration
循环处理的
for
异常。 但它不会引发
GeneratorExit
,因为当调用
close
时,
gen
执行已经结束。


有趣的是,

GeneratorExit
不是继承自
Exception
,而是继承自
BaseException
。 如果您写
except Exception: print('hi')
,则只会打印
1
。 这样,您就可以管理生成器中引发的任何异常,而无需中断其执行:

def gen():
    for i in (1, 2):
        try:
            yield i
        except Exception:
            pass

如果您使用

except: pass
来代替,它将捕获
GeneratorExit
并允许执行
gen
继续产生另一个值。 根据
generator.close
文档,这不应该发生:

如果生成器产生一个值,则会引发

RuntimeError

事实上,你会得到: RuntimeError:生成器忽略了 GeneratorExit

© www.soinside.com 2019 - 2024. All rights reserved.