生成器函数
生成器函数(Generator Function)是创建迭代器的简洁方式。它使用普通函数语法,但用 yield 代替 return,每次产生一个值后暂停执行,下次从暂停处继续。这让惰性计算、无限序列、大数据流处理变得简单自然。
基本语法
def countdown(n):
while n > 0:
yield n
n -= 1
# 使用
for num in countdown(5):
print num,
# 输出:5 4 3 2 1
countdown 是生成器函数。调用它不会执行函数体,而是返回一个生成器对象(迭代器):
gen = countdown(5)
print gen # <generator object countdown at 0x...>
print gen.next() # 5
print gen.next() # 4
print gen.next() # 3
print gen.next() # 2
print gen.next() # 1
print gen.next() # StopIteration
yield 的执行流程
def simple_generator():
print "Start"
yield 1
print "After 1"
yield 2
print "After 2"
yield 3
print "End"
g = simple_generator()
print g.next() # Start, 1
print g.next() # After 1, 2
print g.next() # After 2, 3
print g.next() # End, StopIteration
每次调用 next():
- 函数从上次
yield处继续执行(或从头开始) - 执行到下一个
yield,产生值,暂停 - 如果没有更多
yield,函数结束,抛出StopIteration
生成器的状态
生成器有三种状态:
- GEN_CREATED:创建后未启动
- GEN_RUNNING:正在执行
- GEN_CLOSED:执行完毕或主动关闭
g = countdown(3)
print g.gi_frame.f_lineno # 函数当前行号
g.close() # 提前关闭生成器
g.next() # StopIteration(或 RuntimeError)
生成器与列表的对比
# 列表:一次性计算所有值
def squares_list(n):
return [x * x for x in range(n)]
# 生成器:惰性计算
def squares_gen(n):
for x in range(n):
yield x * x
# 内存对比
import sys
print sys.getsizeof(squares_list(1000000)) # 约 8MB
print sys.getsizeof(squares_gen(1000000)) # 约 80 字节
生成器不存储所有值,适合处理大数据流。
实际应用
读取大文件:
def read_large_file(filename):
with open(filename, "r") as f:
for line in f:
yield line.strip()
# 处理 10GB 文件,内存只占用一行
for line in read_large_file("huge.log"):
if "ERROR" in line:
print line
无限序列:
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
fib = fibonacci()
for i in range(10):
print fib.next(),
# 输出:0 1 1 2 3 5 8 13 21 34
管道处理:
def find_files(pattern):
import glob
for path in glob.glob(pattern):
yield path
def read_lines(files):
for filename in files:
with open(filename, "r") as f:
for line in f:
yield line.strip()
def grep(pattern, lines):
for line in lines:
if pattern in line:
yield line
# 组合成管道
files = find_files("*.log")
lines = read_lines(files)
errors = grep("ERROR", lines)
for error in errors:
print error
send() 方法
Python 2.5+ 支持 send(),向生成器发送数据:
def accumulator():
total = 0
while True:
value = yield total
if value is None:
continue
total += value
acc = accumulator()
print acc.next() # 0(启动生成器)
print acc.send(10) # 10
print acc.send(20) # 30
print acc.send(5) # 35
send(value) 把值赋给 yield 表达式,然后继续执行到下一个 yield。
throw() 和 close()
def generator():
try:
yield 1
yield 2
except ValueError:
yield "Caught"
g = generator()
print g.next() # 1
g.throw(ValueError, "test") # Caught
throw() 在生成器内部抛出异常,close() 抛出 GeneratorExit 让生成器清理资源。
生成器 vs 迭代器类
| 方式 | 代码量 | 状态管理 | 适用场景 |
|---|---|---|---|
| 迭代器类 | 多 | 手动 | 复杂状态 |
| 生成器函数 | 少 | 自动(yield) | 简单到中等 |
# 迭代器类(繁琐)
class CountDown(object):
def __init__(self, n):
self.n = n
def __iter__(self):
return self
def next(self):
if self.n <= 0:
raise StopIteration
self.n -= 1
return self.n + 1
# 生成器函数(简洁)
def countdown(n):
while n > 0:
yield n
n -= 1