是的,yield from 在收到 GeneratorExit 时会主动调用子生成器的 close() 方法,前提是子生成器支持该方法;若不支持则静默忽略,若 close() 抛出非 GeneratorExit 异常,父生成器立即中止。

yield from 遇到 GeneratorExit 时,子生成器的 close() 是否被调用?
是的,yield from 在收到 GeneratorExit 时,会**主动调用子生成器的 close() 方法**,前提是子生成器支持该方法(即实现了 __iter__ 且有 close 属性或方法)。
这是 Python 的标准行为,目的是触发子生成器的清理逻辑(比如 finally 块、contextlib.closing、手动注册的退出回调等)。
但注意:这个调用发生在父生成器自身抛出 GeneratorExit 之前,且仅发生一次;如果子生成器在 close() 过程中又抛出异常(非 GeneratorExit),父生成器会直接终止,不继续执行后续逻辑。
子生成器未定义 close() 或 close() 抛异常时会发生什么?
如果子生成器对象没有 close 方法(例如普通迭代器如 range(3).__iter__()),yield from 会静默忽略,不报错,也不做额外清理。
如果子生成器有 close(),但它在执行过程中抛出**非 GeneratorExit 异常**(比如 ValueError),父生成器会立即停止,并把该异常向上冒泡 —— 此时父生成器自己的 finally 仍会执行,但子生成器后续的清理可能已中断。
-
yield from不会捕获子生成器close()中抛出的异常 - 子生成器若在
close()中 raiseGeneratorExit,会被忽略(符合协议) - 子生成器若在
close()中 raise 其他异常,父生成器立刻中止,无法保证完整退出流程
如何确保 yield from 的清理逻辑可靠?
不能只依赖子生成器的 close() 自动调用。尤其当子生成器是自定义类、协程或封装了资源时,应显式控制生命周期。
推荐做法:
- 让子生成器继承
collections.abc.Iterator并明确定义close(),且内部只 raiseGeneratorExit或吞掉其他异常 - 在父生成器中用
try/finally包裹yield from,把关键清理放在父级finally里 - 避免在子生成器的
close()中执行不可重入或状态敏感的操作(比如重复释放文件句柄) - 调试时可加日志:在子生成器的
close()和父生成器的finally中都 print,观察实际调用顺序
一个能验证清理顺序的小例子
运行下面这段代码,你会看到输出严格按以下顺序出现:
sub gen created about to yield from received GeneratorExit sub gen close() called parent finally executed
这说明:yield from 确实先触发子 close(),再执行父级 finally,最后才让 GeneratorExit 终止父生成器。
关键点在于:子生成器的 close() 是同步、阻塞、且不可跳过的一步;但它的可靠性完全取决于子生成器自身的实现健壮性。










