本文目录导读:

Python脚本生成器通过惰性求值(Lazy Evaluation)来节省内存,与列表一次性加载所有数据不同,生成器只在需要时才生成下一个元素,因此占用的是常数量级的内存。
核心原理
对比示例
# 列表方式 - 一次性加载100万数据到内存 my_list = [x * 2 for x in range(1_000_000)] # 约8MB内存 # 生成器方式 - 按需生成,仅占用几十字节 my_generator = (x * 2 for x in range(1_000_000)) # 约56字节
主要节省内存的场景
处理大文件时
# 错误方式:一次读入所有行
with open('large_file.txt') as f:
lines = f.readlines() # 全部加载到内存
# 正确方式:逐行读取(生成器行为)
with open('large_file.txt') as f:
for line in f: # f本身是生成器
process(line)
大数据流处理
# 传统方式
def get_data():
results = []
for i in range(1_000_000):
results.append(compute(i))
return results
# 生成器方式
def get_data_generator():
for i in range(1_000_000):
yield compute(i)
# 使用生成器
for data in get_data_generator():
process(data)
无限序列
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# 永远不会内存溢出
fib = fibonacci()
for _ in range(1000):
print(next(fib))
实际节省内存测试
import sys
# 创建同等规模的列表和生成器
list_comprehension = [x for x in range(100_000)]
generator_expression = (x for x in range(100_000))
print(f"列表占用: {sys.getsizeof(list_comprehension)} bytes")
print(f"生成器占用: {sys.getsizeof(generator_expression)} bytes")
# 输出:
# 列表占用: 824464 bytes (约0.8MB)
# 生成器占用: 56 bytes
高级内存节省技巧
使用itertools
from itertools import islice, count, cycle
# islice:切片生成器,不创建中间列表
for item in islice(generator, 100):
print(item)
# count:无限计数器
for i in islice(count(10), 5):
print(i) # 10, 11, 12, 13, 14
管道式处理
def read_file(filename):
with open(filename) as f:
for line in f:
yield line.strip()
def filter_lines(lines):
for line in lines:
if 'ERROR' in line:
yield line
def parse_line(lines):
for line in lines:
yield parse(line)
# 链式处理,全程零内存开销
for error in parse_line(filter_lines(read_file('large.log'))):
report(error)
递归使用生成器
def tree_walk(node):
yield node
for child in node.children:
yield from tree_walk(child) # Python 3.3+
注意事项
不能重复使用
gen = (x for x in range(5)) print(list(gen)) # [0, 1, 2, 3, 4] print(list(gen)) # [] 已耗尽
需要重新生成
# 需要多次迭代时,需要重新创建生成器
for _ in range(3):
for item in (x for x in range(5)):
print(item, end=' ')
print()
# 输出: 0 1 2 3 4
# 0 1 2 3 4
# 0 1 2 3 4
不能随机访问
gen = (x for x in range(5)) # gen[2] # TypeError: 'generator' object is not subscriptable
何时使用生成器
| 场景 | 推荐方式 |
|---|---|
| 处理大文件 | 生成器 |
| 无限数据流 | 生成器 |
| 需要索引访问 | 列表 |
| 多次迭代 | 列表或重新创建生成器 |
| 数据量小(<1000) | 两者均可 |
性能权衡
- 内存节省:生成器完胜
- CPU效率:生成器略快(减少了内存分配开销)
- 灵活性:列表胜出(支持索引、切片等)
当处理大数据集、流式数据、不确定数据规模时,生成器是节省内存的最佳选择,对于小数据或需要频繁随机访问的场景,使用列表更合适。