Python 编程 - 生成器表达式
生成器表达式(Generator Expression) 是 Python 3 中一种内存效率极高的迭代器创建方式。它外观类似列表推导式,但使用圆括号 (),核心区别在于惰性求值(Lazy Evaluation)——边迭代边生成值,不会一次性将所有数据加载到内存。
1. 基本语法
(expression for item in iterable if condition)
对比列表推导式:
# 列表推导式(立即生成所有数据,占用内存)
list_comp = [x**2 for x in range(1000)] # 占用 ~8KB 以上
# 生成器表达式(惰性生成,只记录算法)
gen_exp = (x**2 for x in range(1000)) # 占用 ~几百字节
2. 核心特性与使用
生成器表达式返回一个 生成器对象,它既是迭代器也是可迭代对象。
基础遍历:
gen = (i * 2 for i in range(5))
for val in gen:
print(val, end=" ") # 输出: 0 2 4 6 8
手动获取值(使用 next()):
gen = (i for i in range(3))
print(next(gen)) # 0
print(next(gen)) # 1
print(next(gen)) # 2
# print(next(gen)) # 抛出 StopIteration 异常
3. 高级应用与最佳实践
(1) 函数调用中省略多余括号
当生成器表达式作为唯一参数传入函数时,可以省略外层的圆括号,写法更简洁:
# 计算 1 到 1亿 的平方和(内存占用极低)
sum_of_squares = sum(x*x for x in range(100_000_000))
# 而不是 sum((x*x for x in range(...)))
(2) 构建数据管道(流式处理)
生成器可以串联,处理超大文件或无限序列:
# 处理超大日志文件,筛选含 'ERROR' 的行,并提取时间戳
lines = (line for line in open('huge_log.txt')) # 流式读行
errors = (line for line in lines if 'ERROR' in line) # 惰性筛选
timestamps = (line.split()[0] for line in errors) # 惰性提取
for ts in timestamps: # 逐条处理,内存只存当前行
print(ts)
(3) 嵌套循环与笛卡尔积
# 生成坐标对 (0,0) (0,1) ... (2,3)
coords = ((x, y) for x in range(3) for y in range(4))
4. 关键陷阱与注意事项(务必留意)
| 特性 | 说明 | 示例 |
|---|---|---|
| 一次性消费 | 生成器只能遍历一次。遍历完后内部指针耗尽,再次遍历为空。 | gen = (x for x in [1,2,3]); list(gen) # [1,2,3]; list(gen) # [] |
| 不存储索引 | 无法像列表那样通过下标 gen[0] 访问,也不支持 len()。 |
只能顺序迭代。 |
| 延迟计算的副作用 | 如果依赖外部变量,变量值在迭代时才被读取,而非定义时。 | i = 5; gen = (x+i for x in range(3)); i = 10; list(gen) # 输出 [10,11,12] |
| 性能取舍 | 内存占用极低,但每次取值有计算开销。重复遍历多次时,列表推导式更快。 | 数据量小(< 1000)用列表;数据量大或无限流用生成器。 |
5. 生成器表达式 vs yield 函数
- 生成器表达式:适合简单逻辑的单行惰性计算(如
(x*2 for x in seq))。 yield生成器函数:适合复杂逻辑(含循环嵌套、分支判断、状态保持)。
# 等价表达
gen1 = (x**2 for x in range(3))
def gen2():
for x in range(3):
yield x**2
6. 总结速查
- 语法:
( 结果 for 变量 in 可迭代对象 [if 条件] ) - 核心优势:处理海量数据、流式IO时,内存占用极小。
- 黄金法则:如果你只需要迭代一次,且数据量巨大,请使用生成器表达式;如果需要反复使用或随机访问,请使用列表推导式。

浙公网安备 33010602011771号