Generator内部未捕获异常会直接终止迭代,使生成器进入GEN_CLOSED状态,无法继续取值或恢复;需通过三层防护:内部try-except容错、finally资源清理、调用方显式异常处理,并主动close确保资源释放。

Generator 内部未捕获异常会直接终止迭代
生成器函数一旦在 yield 暂停点之后抛出未处理的异常(比如文件读取出错、类型转换失败、网络超时等),该异常不会被“吞掉”,而是立即终止整个生成器状态。下次调用 next() 或 send() 时,不再执行后续逻辑,而是直接抛出该异常,并将生成器置为已耗尽状态(GEN_CLOSED)。这意味着:你无法再从中取值,也无法恢复执行。
常见触发场景与典型错误写法
以下代码看似合理,实则埋下隐患:
- 打开文件后未用 try-finally 包裹,异常发生时文件句柄未释放
- yield 后续逻辑中直接访问可能为 None 的属性,引发 AttributeError
- 在 for 循环内解析 JSON 行时未捕获 json.JSONDecodeError,整条流水线中断
- send() 接收数据后做校验,校验失败 raise ValueError,但调用方没准备捕获
正确应对策略:三层防护机制
第一层:内部兜底(局部容错)
在 yield 前后关键路径加 try-except,对可预期错误降级处理(如跳过脏数据、返回默认值),保持迭代不中断:
- 用
except Exception as e:捕获具体异常,记录日志,然后continue或yield None - 避免裸 except 或静默吞异常,至少要 log.warning
第二层:资源兜底(finally 必执行)
所有外部资源(文件、连接、锁)必须放在 try-finally 中,清理逻辑写在 finally 里,且不能含 yield:
- 即使生成器因异常退出,finally 仍会运行,确保 close()、release() 等被调用
- 切记:finally 块中禁止 yield,否则触发 RuntimeError
第三层:调用方兜底(明确控制流)
外部消费时,用 try-except 显式处理 StopIteration 和其他可能传播出的异常:
- for 循环本身会自动处理 StopIteration,但遇到 ValueError、TypeError 等需额外捕获
- 手动调用 next() 或 send() 时,必须包裹 try-except,尤其注意首次 send 非 None 会报 TypeError
- 收到 StopIteration 后不要再调用 next(),否则行为未定义(新版 Python 抛 StopIteration 或 RuntimeError)
别依赖自动关闭,显式 close 更可靠
虽然 for 循环 break 时 CPython 通常会隐式调用 close(),但这不是语言规范保证的行为。当生成器持有数据库连接、子进程或 socket 时,应主动管理生命周期:
- 在异常分支或提前退出前,显式调用
gen.close() - 用 contextlib.contextmanager 封装生成器,把资源获取和释放统一到 with 块中
- 避免靠
del gen或等待 GC —— 时机不可控,资源泄漏风险高

















