生成器(generator)不仅遵守迭代协议,还在此基础上扩展出双向通信与控制能力,这套机制被称为“生成器协议”(Generator Protocol)。理解它,也就掌握了 Python 最具表现力的控制流模型之一。

一、生成器与迭代器的关系

生成器(generator)是一种特殊的迭代器。

只要函数中包含关键字 yield,调用该函数时不会立即执行,而是返回一个生成器对象。

这个对象实现了迭代器协议:它具有 __iter__() 与 __next__() 方法,因此可以用于 for 循环。

示例:

    print(num)

输出:

3

这里的 count_up_to() 并没有返回一个列表,而是一个生成器对象,它会在每次循环中暂停并保存状态。

二、生成器协议的组成

生成器对象不仅实现了迭代协议的两个方法:

__iter__()

__next__()

还额外定义了三种特殊方法,用于与外部程序交互:

send(value):向生成器发送一个值,作为上一次 yield 表达式的返回结果。

throw(type[, value[, traceback]]):向生成器抛出一个异常。

close():终止生成器的执行,释放其内部资源。

这三者共同构成了“生成器协议”。

三、生成器的工作机制

生成器的运行过程可分为三个阶段:

(1)创建阶段

执行函数体但不运行内部代码,只返回一个生成器对象。

(2)启动阶段

首次调用 next() 或 send(None),执行到第一个 yield 停止。

(3)恢复阶段

每次调用 next() 或 send(value),从上次中断处继续执行。

示例:

print(g.send("小艾"))    # 输出 "你好,小艾!"

执行流程说明:

• 第一次调用 next(),生成器运行到第一个 yield 停止,并返回 "你的名字是?";

• 第二次调用 send("小艾"),这个值会赋给变量 name,然后生成器继续执行下一个 yield。

四、send():双向通信机制

send() 是生成器协议中最独特的部分,它让生成器不仅能“输出数据”,还能“接收数据”。

规则如下:

(1)第一次启动生成器必须使用 next() 或 send(None);

(2)之后可以使用 send(value) 向 yield 传值。

示例:

g.send("Python")    # 输出:收到:Python

这种机制常用于协程、事件驱动系统和数据流控制中。

五、throw() 与异常传播

throw() 方法允许从外部向生成器内部抛出异常。

生成器可以捕获异常并做出响应,也可以不处理,让异常继续向外传播。

print(g.throw(ValueError, "出错了"))  # 捕获异常:出错了

throw() 常用于取消或中断生成器中的长时间任务。

六、close():终止生成器

调用 close() 会向生成器内部抛出 GeneratorExit 异常,用于安全终止。

生成器可选择忽略或捕获它,但不允许再产生新的值。

g.close()

输出:

生成器已关闭

七、生成器返回值与 StopIteration

从 Python 3.3 起,生成器函数可以使用 return 返回一个值。

当生成器结束时,该值会被封装在 StopIteration.value 属性中。

    print(n)

如果使用手动方式调用:

        break

输出:

返回值: 3

八、生成器协议的优势及应用

优势:

惰性求值:按需生成数据,节省内存;

状态保存:自动记录暂停位置与局部变量;

双向通信:通过 send() 传入数据;

灵活终止:throw() 与 close() 可控制执行流;

协程兼容:生成器协议是 Python 协程(async/await)机制的前身。

应用:

数据流处理:日志流、网络流等;

生产者–消费者模型;

协程实现:在 asyncio 出现之前,Python 就用生成器实现了协作式多任务;

惰性计算:如无限序列、延迟加载数据。

小结

生成器协议建立在迭代器协议之上,并进一步扩展了交互能力。

它的四个核心方法是:

__iter__():返回迭代器自身;

__next__():返回下一个元素;

send(value):向生成器传入数据;

throw() 与 close():控制异常与终止。

生成器协议让函数拥有“可暂停、可恢复、可通信”的能力,为异步编程和流式处理奠定了基础。

打开网易新闻 查看精彩图片

点赞有美意,赞赏是鼓励