在 Python 中,生成器(Generator)和列表(List)都是常用的可迭代对象,但它们在底层实现、内存占用、访问方式和使用场景上有本质区别。
核心区别
特性
列表
生成器
存储方式
一次性在内存中创建并存储所有元素
惰性求值,按需生成元素,不保存所有值
内存占用
高(随元素个数线性增长)
低(固定,只保存当前状态)
访问方式
支持索引、切片、重复迭代
只能顺序迭代一次,不支持索引
创建方式
[expr for x in iterable] 或 list()
(expr for x in iterable) 或 yield 函数
迭代次数
可以多次迭代
只能迭代一次,迭代完即停止
速度
生成快(预分配内存),访问快(随机访问)
生成略慢(每次计算),顺序访问快
适用场景
需要多次访问、随机访问、修改元素
处理大数据流、无限序列、节省内存
详细对比
1. 内存占用
-
列表:将每个元素存储在连续的内存空间中。例如
range(1000000)转换为列表会占用约 8MB 内存(每个整数 28 字节)。 -
生成器:只记录当前状态(如当前索引、步长),不保存历史元素。例如
(x for x in range(1000000))只占用几十字节内存。import sys
lst = [x for x in range(1000000)] gen = (x for x in range(1000000))
print(sys.getsizeof(lst)) # 约 8000056 字节 print(sys.getsizeof(gen)) # 约 112 字节(固定)
2. 访问方式
-
列表:支持索引、切片、
len(),可多次迭代。gen = (x for x in [1, 2, 3]) print(next(gen)) # 1 print(next(gen)) # 2
无法使用 gen[0] 或 len(gen)
3. 迭代次数
-
列表:可以重复迭代任意次数。
lst = [1, 2, 3] for i in lst: print(i) # 第一次 for i in gen: print(i) # 第二次
-
生成器:只能迭代一次,迭代完后会自动停止(抛出
StopIteration)。gen = (x for x in [1, 2, 3]) for i in gen: print(i) # 第一次,输出 1 2 3 for i in gen: print(i) # 第二次, 无输出(已耗尽)
4. 创建方式
-
列表:方括号
[]列表推导式,或list()构造函数。lst = [x**2 for x in range(5)] # 列表推导式 lst2 = list(range(5)) # 从可迭代对象构造
-
生成器:圆括号
()生成器表达式,或yield函数。gen = (x2 for x in range(5)) # 生成器表达式 def squares(n): for i in range(n): yield i2 # 生成器函数
5. 性能差异
-
列表:生成时需要预先计算所有元素,如果元素很多或计算复杂,可能耗时且占内存。但生成后访问速度很快(O(1) 索引)。
-
生成器:生成每个元素的延迟很小,但每次迭代都需要重新计算,整体迭代速度可能略慢(但差别不大)。适合处理流式数据或无限序列。
6. 适用场景
-
使用列表:
-
需要多次遍历数据
-
需要随机访问(通过索引)
-
数据量不大,内存充足
-
需要修改、追加、删除元素
-
-
使用生成器:
-
处理超大文件或数据集,避免内存溢出
-
生成无限序列(如斐波那契数列)
-
管道式数据处理(一个生成器的输出作为另一个的输入)
-
只需一次遍历的场景
示例对比
例1:计算平方和
# 列表方式
def sum_squares_list(n):
squares = [x**2 for x in range(n)] # 一次性存储所有平方
return sum(squares)
# 生成器方式
def sum_squares_gen(n):
return sum(x**2 for x in range(n)) # 按需生成,不存储
当 n 很大时,列表方式会占用大量内存,生成器方式几乎没有额外内存开销。
例2:读取大文件
# 列表方式(不推荐)
lines = open('large_file.txt').readlines() # 将所有行加载到内存
# 生成器方式(推荐)
def read_lines(file):
with open(file) as f:
for line in f:
yield line # 每次返回一行
总结
-
列表是 “一次性拿出所有”,适合小数据量或需要多次访问的场景。
-
生成器是 “用的时候再产生”,适合大数据量、流式处理、节省内存的场景。
在实际开发中,应根据具体需求选择:内存允许时用列表更方便;处理海量数据或无限序列时用生成器更明智。