1. 生成器与Yield的底层机制
Python生成器是一种特殊的迭代器,其核心在于yield关键字实现的执行暂停与恢复机制。当函数包含yield语句时,调用该函数不会立即执行代码,而是返回一个生成器对象。这个对象遵循迭代器协议,实现了__iter__()和__next__()方法。
生成器函数执行到yield时会发生以下原子操作:
- 暂停函数执行,保存当前栈帧(包含局部变量、指令指针等)
- 将yield右侧表达式的值作为__next__()的返回值
- 在下次调用__next__()时,从保存的栈帧恢复执行
def countdown(n): print("Starting countdown") while n > 0: yield n n -= 1 print("Blastoff!") # 生成器对象创建时不会执行函数体 gen = countdown(3) print(next(gen)) # 输出: Starting countdown 然后输出 3 print(next(gen)) # 输出 22. 惰性求值的实现原理
生成器通过延迟计算实现了内存高效的数据流处理。与列表等容器类型不同,生成器不会预先生成所有元素,而是在每次迭代时动态计算下一个值。
关键特性对比:
| 特性 | 列表 | 生成器 |
|---|---|---|
| 内存占用 | 存储所有元素 | 只存储当前状态 |
| 计算时机 | 立即计算 | 按需计算 |
| 可重用性 | 可多次迭代 | 单次迭代 |
| 长度确定性 | 已知长度 | 可能无限 |
典型应用场景:
- 处理大型文件时逐行读取
- 生成无限序列(如斐波那契数列)
- 数据管道中的中间处理环节
# 文件处理对比 def read_lines_list(file): with open(file) as f: return f.readlines() # 立即读取所有行到内存 def read_lines_gen(file): for line in open(file): yield line # 每次迭代只读取一行3. 生成器表达式与语法糖
生成器表达式是创建生成器的简洁语法,形式为(expr for item in iterable)。与列表推导式不同,它不会构建完整列表,而是按需生成值。
# 平方数生成器 squares = (x*x for x in range(1000000)) # 不占用内存 sum_squares = sum(x*x for x in range(1000000)) # 更高效的内存使用注意事项:
- 生成器表达式只能被消费一次
- 避免在生成器表达式内修改外部变量
- 复杂逻辑建议使用完整的生成器函数
# 多层过滤示例 filtered = ( x.upper() for x in open('data.txt') if not x.startswith('#') and len(x.strip()) > 0 )4. 高级生成器控制方法
除了基本的__next__(),生成器还支持更精细的控制:
4.1 send()方法
允许向生成器内部传递值,该值会成为yield表达式的返回值
def accumulator(): total = 0 while True: value = yield total if value is None: break total += value gen = accumulator() next(gen) # 启动生成器,返回0 print(gen.send(1)) # 输出1 print(gen.send(5)) # 输出64.2 throw()方法
向生成器内部抛出指定异常
def resilient_gen(): try: while True: try: yield 42 except ValueError: print("Handled ValueError") except GeneratorExit: print("Cleaning up") gen = resilient_gen() next(gen) # 返回42 gen.throw(ValueError) # 输出"Handled ValueError"并继续4.3 yield from语法
PEP 380引入的语法,用于简化生成器委托
def chain(*iterables): for it in iterables: yield from it # 等价于 for item in it: yield item list(chain('ABC', range(3))) # ['A','B','C',0,1,2]5. 生成器的实际应用模式
5.1 协程模拟
生成器可以用于实现简单的协程模式:
def coroutine(func): def start(*args, **kwargs): cr = func(*args, **kwargs) next(cr) # 启动协程 return cr return start @coroutine def grep(pattern): print(f"Looking for {pattern}") while True: line = (yield) if pattern in line: print(line) g = grep("python") g.send("Yeah, but no, but yeah, but no") g.send("python generators rock!") # 会打印这行5.2 状态机实现
生成器天然适合实现状态机:
def traffic_light(): while True: print("Red") yield print("Yellow") yield print("Green") yield light = traffic_light() next(light) # Red next(light) # Yellow5.3 数据管道
构建高效的数据处理管道:
def read_files(filenames): for name in filenames: with open(name) as f: yield from f def filter_comments(lines): for line in lines: if not line.strip().startswith('#'): yield line def uppercase(lines): for line in lines: yield line.upper() pipeline = uppercase(filter_comments(read_files(['log1.txt', 'log2.txt']))) for line in pipeline: process(line)6. 性能优化与陷阱
6.1 内存效率测试
import sys from timeit import timeit # 列表推导式 list_comp = [x*x for x in range(1000000)] print(f"列表内存占用: {sys.getsizeof(list_comp)/1024/1024:.2f} MB") # 生成器表达式 gen_exp = (x*x for x in range(1000000)) print(f"生成器内存占用: {sys.getsizeof(gen_exp)} bytes") # 执行时间对比 print("列表构建时间:", timeit('[x*x for x in range(1000000)]', number=10)) print("生成器构建时间:", timeit('(x*x for x in range(1000000))', number=10))6.2 常见陷阱与解决方案
- 过早耗尽问题:
numbers = (x for x in range(3)) print(sum(numbers)) # 6 print(sum(numbers)) # 0 (生成器已耗尽)- 变量作用域问题:
# 错误示例 gen = (lambda: x for x in range(3)) print([g() for g in gen]) # 输出[2,2,2]而非[0,1,2] # 正确写法 gen = (lambda x=x: x for x in range(3))- 资源清理:
def read_db(): try: db = connect_to_database() for record in db.query(): yield record finally: db.close() # 确保资源释放 # 使用contextlib.closing确保资源清理 from contextlib import closing with closing(read_db()) as gen: for item in gen: process(item)7. 异步生成器(Python 3.6+)
Python 3.6引入了异步生成器,使用async for和await语法:
import asyncio async def async_counter(n): for i in range(n): await asyncio.sleep(1) yield i async def main(): async for i in async_counter(5): print(f"{i} seconds passed") asyncio.run(main())关键区别:
- 使用
async def定义 - 包含
yield和await - 必须使用
async for迭代 - 实现了
__aiter__和__anext__方法