登录
首页 >  文章 >  python教程

生成器如何用 throw() 抛异常到内部

时间:2026-04-07 16:04:18 298浏览 收藏

Python生成器的`throw()`方法并非万能异常注入工具,其核心限制在于:只有包含`yield`表达式(如`received = yield value`)的生成器才能真正捕获并处理外部抛入的异常;若仅使用`yield`语句,`throw()`只会粗暴终止生成器而无法进入`except`块。这一机制要求开发者精确控制暂停点、确保生成器处于可中断状态(已启动且正在`yield`处等待),并在`try/except`结构中合理包裹`yield`表达式——稍有不慎就会遭遇`StopIteration`冒泡、`RuntimeError`或异常静默失败。文章通过对比正误写法、剖析控制流逻辑,并以可重置计数器为例,揭示了`throw()`在协程式状态管理中的实用价值与典型陷阱,帮你避开那些让调试陷入迷雾的“看似合理却失效”的异常处理设计。

如何让生成器支持 throw() 抛异常到生成器内部

生成器必须用 yield 表达式而非 yield 语句

Python 中,只有函数体内包含 yield 表达式(即出现在等号右侧、能接收值的 yield),才能通过 throw() 向内部抛异常并被 except 捕获。如果只写 yield 语句(如 yield 42),它仍可迭代,但 throw() 只会终止生成器,无法进入 except 块。

常见错误现象:Generator.throw() 调用后直接抛出 StopIteration 或原异常未被捕获,本质是控制流没停在可中断的 yield 表达式上。

  • 正确写法:received = yield value —— 此处 yield 是表达式,暂停点可接收值或异常
  • 错误写法:yield value(无赋值)—— 控制流经过即继续,throw() 无法“插入”到执行中
  • 即使不打算接收值,也应写成 _ = yield valueyield value; continue 后补一个空 yield 表达式

throw() 必须在生成器暂停时调用

throw() 不是“发消息”,而是向当前暂停点注入异常。若生成器已结束(StopIteration 已 raise)、尚未启动(还没第一次 next()send(None)),或正在运行中(比如卡在某个循环里没遇到 yield),调用 throw() 都会报 RuntimeError: generator already executing 或直接失败。

使用场景:常用于协程式状态机,比如等待用户输入时,外部想强制中断并清理资源。

  • 首次启动必须用 next(gen)gen.send(None),否则 throw() 无效
  • 每次 throw() 后,生成器必须再次被驱动(next()send()),否则停留在抛异常后的暂停点
  • 若生成器在 try/finally 中,throw() 触发异常时 finally 仍会执行

异常类型需与 except 匹配,且不能跳过 yield

生成器内 except 能捕获 throw() 的异常,前提是该 except 所在的 try 块覆盖了当前 yield 表达式所在的位置,并且异常类型一致。Python 不支持“跳转式捕获”——异常不会向上越过未激活的 try 块。

参数差异:gen.throw(TypeError, "msg") 等价于 gen.throw(TypeError("msg")),但推荐传入已实例化的异常对象,避免某些版本兼容问题。

  • yieldtry 外,throw() 会直接冒泡,不会进 except
  • except BaseException: 可捕获所有,但会吞掉 GeneratorExitSystemExit,慎用
  • 捕获后若不 returnyield,生成器立即结束;若继续 yield,则恢复迭代流程

实际例子:带错误恢复的计数生成器

下面是一个能响应 throw(ValueError) 并重置计数的生成器:

def counter():
    i = 0
    while True:
        try:
            i += 1
            # 必须是 yield 表达式,哪怕不接收值
            _ = yield i
        except ValueError:
            print("resetting...")
            i = 0

调用方式:

g = counter()
next(g) # → 1
next(g) # → 2
g.throw(ValueError) # 打印 resetting...,不抛异常
next(g) # → 1(已重置)

容易踩的坑:忘记在 throw() 后调用 next()send(),结果生成器停在 except 块末尾,下一次 next() 才真正产出值——这个延迟行为常被忽略。

终于介绍完啦!小伙伴们,这篇关于《生成器如何用 throw() 抛异常到内部》的介绍应该让你收获多多了吧!欢迎大家收藏或分享给更多需要学习的朋友吧~golang学习网公众号也会发布文章相关知识,快来关注吧!

资料下载
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>