def gen():
try:
yield 1
yield 2
except:
print('hi')
def func():
for x in gen():
return x
print(func())
此代码打印
hi
,然后打印 1
。为什么它不直接打印 1
?引发了什么异常?
您的
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