生成器中try-except具有块级作用域隔离能力,异常仅在两次yield间局部生效,不影响外部变量和下次迭代;需确保所有分支含yield或return,throw()可主动注入异常,finally比except更可靠用于资源清理。

生成器内部的 try-except 不是简单“捕获异常”,而是天然具备块级作用域隔离能力——它让错误处理逻辑和宿主函数的变量生命周期互不干扰。关键在于:异常只在 yield 暂停点之间局部生效,不会穿透到生成器外部,也不会污染调用方的变量状态。
yield 前后变量不受 except 干扰
生成器中每个 yield 是一个执行断点,try-except 的作用范围严格限定在两次 yield 之间。即使 except 块里修改了局部变量,这些变更也仅影响当前迭代周期,下一次 next() 或 send() 会从干净的上下文重新进入循环。
- 例如,在
for i in range(3): try: ... yield ... except: i = -1中,i = -1不会影响下次循环的i取值,因为 for 循环每次都会重赋值 - 若用 while + 手动计数器,需注意变量是否在 except 中被意外覆盖;推荐把状态变量放在 try 外、yield 内,或用不可变结构(如 tuple)传递中间结果
避免在 except 里漏掉 yield 导致提前退出
生成器一旦没有 yield 就结束(无论是 return 还是自然落出函数),外部迭代就会收到 StopIteration。很多报错“突然中断”的问题,根源是 except 块里没写 yield,导致控制流直接退出函数体。
- 正确做法:所有分支路径(包括异常处理分支)都应有明确的 yield 或 return
- 常见模式:
except ValueError: yield "fallback"或except Exception as e: logging.warning(...); yield None - 若想跳过当前项不产出任何值,可用
continue(配合 while 循环)或显式yield空值,而非静默结束
用 send() + throw() 主动注入异常,实现可控恢复
外部可通过 gen.throw(TypeError) 向生成器当前暂停点注入异常,此时生成器会在该 yield 行恢复并触发对应 except。这种机制让错误响应完全由生成器内部逻辑决定,宿主函数无需维护额外状态变量来标记“上次是否出错”。
- 适合场景:资源临时不可用时降级、输入校验失败后切换策略、网络请求超时后重试
- 注意:throw() 调用后,生成器必须有能捕获该异常类型的 except,否则异常会向上冒泡到调用方
- 与普通 try-except 不同,throw() 是双向通信信号,不依赖数据流,也不改变生成器已有变量值
清理资源时优先用 finally,而非 except
如果生成器持有文件句柄、数据库连接等资源,finally 块比 except 更可靠——无论是否发生异常、是否被 catch、甚至用户调用 close(),finally 都会执行。
- 错误写法:
except: f.close(); raise—— 若 close() 本身出错,资源可能泄漏 - 推荐写法:
try: yield data finally: f.close(),确保释放动作总被执行 - 更稳妥的是结合上下文管理器:
with open(...) as f: yield from process(f)











