Python 列表推导式与生成器对比
Python 列表推导式与生成器对比
一、两者解决的都是“批量转换”
处理一个序列、按规则生成另一个序列时,列表推导式和生成器是 Python 最常用的两种写法。它们语法相近,但内存模型完全不同:一个一次性把结果装进列表,一个按需逐个产出。
二、列表推导式
方括号 [] 包裹,立刻计算并返回完整列表:
# 把 0~9 的偶数平方放进列表
result = [x * x for x in range(10) if x % 2 == 0]
print(result) # [0, 4, 16, 36, 64]
特点:所有结果同时占内存,可以反复索引、len()、切片;适合数据量小、需要多次访问的场景。
三、生成器表达式
圆括号 () 包裹,返回一个生成器对象,不立即计算:
g = (x * x for x in range(10) if x % 2 == 0)
print(g) # <generator object ...>
print(next(g)) # 0
print(list(g)) # [4, 16, 36, 64]
每调用一次 next 才算下一个值,用完即弃,内存恒定,适合大数据流、文件逐行处理。
四、生成器函数 yield
用 yield 定义的函数也是生成器:
def squares(n):
for x in range(n):
yield x * x
for v in squares(5):
print(v) # 0 1 4 9 16
函数执行到 yield 暂停,返回值并记住现场,下次从暂停处继续,不会一次性构造列表。
五、内存与性能对比
import sys
a = [x for x in range(1_000_000)]
b = (x for x in range(1_000_000))
print(sys.getsizeof(a)) # 占用很大(约 8MB 级别,随数据增长)
print(sys.getsizeof(b)) # 固定很小(生成器对象本身)
- 列表:O(n) 内存,可随机访问,计算一次性完成;
- 生成器:O(1) 内存,只能顺序迭代,且只能遍历一次;
- 生成器不能 len()、不能切片,遍历完就空了。
六、怎么选
- 结果要复用、要排序、要索引、数据量小 → 列表推导式;
- 处理超大文件、无限流、管道式计算 → 生成器;
- 直接传给 sum、max、join 这类消费函数时,用生成器更省内存:
total = sum(x * x for x in range(1_000_000))
七、常见误区
- 以为生成器能反复遍历,第一次 for 循环后再取就是空;
- 大数据还套列表推导式,直接内存爆满;
- 把生成器当列表用 len(),报 TypeError;
- 嵌套推导式过度复杂,可读性下降,不如拆成普通循环;
- 生成器内部异常会在 next 时才暴露,调试时机靠后。
八、总结
列表推导式用 [] 一次性产出完整列表,适合小数据与重复访问;生成器表达式用 () 或 yield 按需产出,内存恒定但只能顺序消费一次。选择依据是数据规模和是否需要复用:小而多用列表,大而流式用生成器,传给归约函数优先用生成器省内存。