type
status
date
slug
summary
tags
category
icon
password
📝 高级生成器特性
send(value) → 把 value 赋给当前 yield 表达式 → 继续跑字节码直到下一个 yield 或 return。close() → 字节码插入 RAISE_VARARGS GeneratorExit → 如果生成器捕获 GeneratorExit 并 return,则正常结束;否则隐式 StopIteration。throw() → 字节码插入 RAISE_VARARGS 指定异常,其余同 send。1、生成器的 send(value) 方法
作用:把
value 注入到 当前 yield 表达式 并继续执行到下一个 yield参数:任意对象
返回值:下一个
yield 产出的值典型异常:
StopIteration1.1、概念
send(value) 把 外部数据 通过 yield 表达式 注入 到生成器内部,并立即恢复生成器执行,直至下一个 yield 或 StopIteration。1.2、基本语法
1.3、运行步骤逐帧跟踪
行号 | 生成器代码 | 外部调用 | 生成器状态 | 返回值 |
1 | received = yield 1 | next(gen) | 停在 yield 1 | 1 |
2 | print(received) | gen.send("Hello") | 继续执行 | None |
3 | yield 2 | 同上 | 停在 yield 2 | 2 |
4 | 函数结束 | 同上 | 抛出 StopIteration | \- |
1.4、send() 与 next() 的区别
方法 | 是否注入值 | 首次启动 | 典型用途 |
next(gen) | 否 | ✅ | 仅仅推进 |
gen.send(None) | 否 | ✅ | 同 next |
gen.send(v) | 是 | ❌ | 双向通信 |
1.5、异步场景——“协程雏形”
- 在
async/await出现前,生成器 +send()就是 协程 的底层实现:
yield 暂停 I/O;事件循环用
send(result) 把 I/O 结果送回生成器,从而“异步”继续执行 。1.6、完整示例:计数协程 + 外部注入
1.7、常见陷阱
- 首次忘记
next()
直接
gen.send(123)会抛 TypeError,因为生成器尚未启动。- 在
yield前return
send() 之后若立即return,将抛出 StopIteration(value),value 即 return 的值。- 循环变量闭包
在列表推导式里使用
send() 时要注意延迟绑定问题,与 lambda 闭包类似。2、生成器的 close() 方法
作用:立即在生成器内部抛出
GeneratorExit,可做清理参数:无
返回值:
None典型异常:若生成器
yield 后未捕获,则隐式 StopIteration3、生成器的 throw(type[, value[, traceback]]) 方法
作用:在生成器内部抛出指定异常并继续执行
参数:异常类或实例
返回值:下一个
yield 产出的值典型异常:同
send,但会抛出你给定的异常4、生成器表达式
5、yield from 委托
5.1、基本语法
5.2、工作原理
- 当执行到
yield from iterable时,Python 会: - 遍历
iterable中的每个元素。 - 将每个元素依次
yield出去。 - 如果
iterable是一个生成器,那么yield from会捕获生成器的StopIteration异常(该异常在生成器耗尽时抛出),并正常结束外层生成器。
5.3、示例
5.4、高级用法
5.5、与 yield 的区别
yield:用于从生成器中返回一个值,并在下一次调用 next() 时从上次停止的地方继续执行。yield from:用于将一个生成器或可迭代对象的输出转发到另一个生成器。它允许生成器委托给另一个生成器,从而简化代码。📝 生成器的实际应用场景
1、处理大型文件/数据集
1.1、概念
生成器把“一次性读入”变成“按需流式读取”,只保留当前一行(或一块)在内存,从而 O(1) 内存 处理 GB 级文件。
1.2、内存模型对比
方式 | 内存峰值 | 说明 |
f.read() 或 list(f) | 文件大小 | 内容全部加载 |
for line in f: | 一行 | 内建缓冲区 |
yield 生成器 | 一行 + 对象 | 手动控制块大小,可再压缩 |
1.3、三种典型场景
- 逐行文本
- 分块二进制
- 数据库 / 网络流
1.4、逐行示例:统计 1 GB 日志里 ERROR 行数
1.5、分块二进制:SHA-256 大文件
1.6、性能 & 高级技巧
技巧 | 代码片段 | 效果 |
缓冲大小 | f.read(size) | 调整 I/O 块大小 |
惰性转换 | (int(x) for x in lines) | 零拷贝转换 |
多文件链 | yield from file_gen | 无缝拼接 |
并发 | yield from asyncio_stream() | 与协程配合 |
1.7、完整实战:CSV → 清洗 → 入库(百万行)
2、无限序列
2.1、概念
把
while True: 和 yield 写在一起,就能得到一个按需生产、永不枯竭的数据流;外部只需按需 next() 或 for x in gen: 拿值,内存始终 O(1)。2.2、最小骨架
2.3、内存优势对比
方式 | 存储 | 内存峰值 |
list(range(10**8)) | 一次性加载 | ≈ 3 GB |
无限 yield | 按需生产 | ≈ 几十字节 |
2.4、4 类经典无限序列
- 自然数
- 斐波那契
- 等差 / 等比序列
- 素数流(埃拉托斯特尼筛)
2.5、截取技巧
需求 | 工具 | 示例 |
前 N 个 | itertools.islice | list(islice(gen, 10)) |
满足条件的前 N 个 | itertools.takewhile | list(takewhile(lambda x: x < 100, gen)) |
跳过前 N 个 | itertools.dropwhile | list(dropwhile(lambda x: x < 100, gen)) |
2.6、实战案例:无限素数流 → 前 10 个 4k+1 型素数
3、数据管道
3.1、一句话定义
“生成器数据管道”就是把一连串生成器像 Unix 管道一样串起来:上游不断
yield,下游不断 for x in upstream 或 yield from,数据像水流一样逐层传递,内存占用极低。3.2、最小骨架
3.3、运行步骤逐帧图
数据按需生产,下游用到多少就生产多少。
任何阶段随时
break,上游立即停止,零浪费。3.4、三种连接方式对比
连接方式 | 语法 | 特点 |
普通 for | for x in g: yield f(x) | 最通用,可额外逻辑 |
yield from | yield from g | 一行代理,无额外开销 |
生成器表达式 | (f(x) for x in g) | 极简,但只能单行表达式 |
3.5、性能对比
方式 | 内存峰值 | 时间 |
列表推导式 | 全部加载 | 高 |
生成器管道 | O(1) | 低 |
4、状态机实现
生成器函数的 执行位置 + 局部变量 被解释器保存在 frame + cell 里,每次
next() / send() 从上次暂停处继续跑,本质就是 “带记忆的 goto”。📝 生成器与协程
生成器在 Python 3.5+ 中被扩展为协程,用于异步编程。虽然生成器和协程在底层共享很多机制,但它们的用途和语义有所不同。