Python函数-生成器拓展
2025-8-31
| 2025-8-31
Words 3217Read Time 9 min
type
status
date
slug
summary
tags
category
icon
password

📝 高级生成器特性

send(value) → 把 value 赋给当前 yield 表达式 → 继续跑字节码直到下一个 yieldreturn
close() → 字节码插入 RAISE_VARARGS GeneratorExit → 如果生成器捕获 GeneratorExitreturn,则正常结束;否则隐式 StopIteration
throw() → 字节码插入 RAISE_VARARGS 指定异常,其余同 send

1、生成器的 send(value) 方法

作用:把 value 注入到 当前 yield 表达式 并继续执行到下一个 yield
参数:任意对象
返回值:下一个 yield 产出的值
典型异常StopIteration

1.1、概念

send(value)外部数据 通过 yield 表达式 注入 到生成器内部,并立即恢复生成器执行,直至下一个 yieldStopIteration

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,因为生成器尚未启动。
  • yieldreturn
    • send() 之后若立即return,将抛出 StopIteration(value)valuereturn 的值。
  • 循环变量闭包
    • 在列表推导式里使用 send() 时要注意延迟绑定问题,与 lambda 闭包类似。

2、生成器的 close() 方法

作用:立即在生成器内部抛出 GeneratorExit,可做清理
参数:无
返回值None
典型异常:若生成器 yield 后未捕获,则隐式 StopIteration

3、生成器的 throw(type[, value[, traceback]]) 方法

作用:在生成器内部抛出指定异常并继续执行
参数:异常类或实例
返回值:下一个 yield 产出的值
典型异常:同 send,但会抛出你给定的异常

4、生成器表达式

5、yield from 委托

5.1、基本语法

5.2、工作原理

  • 当执行到 yield from iterable 时,Python 会:
      1. 遍历iterable 中的每个元素。
      1. 将每个元素依次 yield 出去。
      1. 如果 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. 逐行文本
  1. 分块二进制
  1. 数据库 / 网络流

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 upstreamyield 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+ 中被扩展为协程,用于异步编程。虽然生成器和协程在底层共享很多机制,但它们的用途和语义有所不同。
        • 开发
        • Python
        • Python函数-闭包拓展Python函数-循环变量闭包陷阱详解
          Loading...