.throw() 是同步将异常注入当前 yield 暂停点,能否捕获取决于 yield 是否在 try 块内;未捕获则生成器关闭,资源清理须依赖 finally 而非 except。

必须把 yield 放进 try 块里
生成器不会自动捕获 `.throw()` 注入的异常。只有当 `yield` 表达式本身处于 `try` 语句的监控范围内,且对应 `except` 能匹配传入异常时,才能响应。- 错误写法:`yield "ready"` 写在 `while True:` 循环开头,外层只有 `try` 包整个函数体——这无法捕获,因为异常发生在 yield 点,而 `except` 在函数入口之后、yield 之前
- 正确写法:`try: x = yield "ready" except ValueError: ...` —— yield 必须是 try 块内的直接语句
- 注意:`except GeneratorExit` 必须显式 `raise`,否则会被静默忽略(这是协议要求)
别依赖外部 try-catch 拦截 throw 的效果
你在调用方写 `try: g.throw(...)` 并不能让生成器内部“变得可捕获”。`.throw()` 抛出的异常是否终止生成器,完全取决于生成器内部是否处理了它。外部 try 只能捕获 `.throw()` 方法自身失败(比如对已关闭生成器调用),而不是它注入的异常。- 若生成器内部没 catch,`.throw()` 会立即抛出原异常(如 `ValueError`),并使生成器状态变为 `CLOSED`
- 若生成器内部成功 catch 并 `yield` 或 `return`,`.throw()` 调用本身会返回下一个产出值,不抛异常
- 所以“防范”的重点永远在生成器定义侧,不在调用侧
配合 finally 做资源兜底,避免清理遗漏
即使你用 try-except 捕获了 `.throw()` 注入的异常,也不能保证资源一定释放。比如打开文件后在 yield 处被 `.throw()` 中断,except 块可能跳过清理逻辑。- 所有关键资源(文件、连接、锁)必须放在 `try` 内部,并配 `finally` 块执行关闭
- 不要只靠 `except` 清理:`except` 只响应特定异常;`finally` 才确保无论 `.throw()`、`send()` 出错还是正常结束,都执行
- 示例:`try: f = open(...); yield ... finally: f.close()` —— 这样即使 `.throw(RuntimeError)` 进来,文件也会关
主动测试 throw 路径,别等线上崩
很多生成器只测了 `next()` 和 `send()` 流程,从不调用 `.throw()`,导致异常路径未经验证。建议在单元测试中显式覆盖。- 对每个自定义异常类型,写一个 `g.throw(MyCustomError)` + 断言后续行为(是否 yield 恢复值、是否打印日志、是否释放资源)
- 特别检查 `GeneratorExit`:调用 `g.close()` 后再 `g.throw(...)` 应该报 `RuntimeError: generator ignored GeneratorExit`
- 用 `inspect.getgeneratorstate(g)` 辅助判断生成器当前是否 `GEN_SUSPENDED`,确保 `.throw()` 调用时机合法











