引言
在 Python 中,列表推导式(list comprehension)和生成器表达式(generator expression)是两种常用的创建序列的方式。它们语法相似,但行为差异显著,理解这些差异有助于写出更高效、更符合需求的代码。
语法对比
列表推导式使用方括号 []:
squares_list = [x**2 for x in range(10)]
生成器表达式使用圆括号 ():
squares_gen = (x**2 for x in range(10))
两者都支持条件过滤和多重循环,例如 [x for x in range(20) if x % 2 == 0] 与 (x for x in range(20) if x % 2 == 0)。
核心区别
1. 内存占用
- 列表推导式会立即计算所有元素,并在内存中创建一个完整的列表。对于大规模数据,可能占用大量内存。
- 生成器表达式是惰性求值的,它返回一个生成器对象,只在迭代时逐个产生元素,几乎不占用额外内存。
例如,处理 100 万个元素时,列表推导式会占用约 8MB 内存(每个整数对象约 28 字节),而生成器表达式只占用少量固定内存。
2. 求值时机
- 列表推导式在定义时立即执行,所有元素一次性生成。
- 生成器表达式在定义时并不执行任何计算,只有在迭代(如
for循环、next())时才逐个产生值。
3. 可迭代性
- 列表是可重复迭代的,可以多次遍历。
- 生成器是一次性的,迭代完一次后即耗尽,再次迭代将得不到任何元素。
gen = (x for x in range(3))
print(list(gen)) # [0, 1, 2]
print(list(gen)) # []
4. 适用场景
- 列表推导式适合:需要多次访问、需要列表方法(如
append、sort)、数据量不大时。 - 生成器表达式适合:数据量大或无限序列、只需遍历一次、作为函数参数(如
sum(x for x in ...))时。
性能考量
对于小规模数据,列表推导式通常更快,因为生成器需要额外的迭代器开销。但对于大规模数据,生成器表达式可以显著减少内存占用,避免内存溢出。在 sum()、max() 等聚合函数中,直接使用生成器表达式可以省去创建中间列表的开销。
总结
| 特性 | 列表推导式 | 生成器表达式 |
|---|---|---|
| 语法 | [...] | (...) |
| 内存 | 占用高 | 占用低 |
| 求值 | 立即 | 惰性 |
| 迭代 | 可重复 | 一次性 |
| 适用 | 小数据、需重用 | 大数据、单次遍历 |
选择哪一种取决于具体需求:如果需要一个完整的列表,用列表推导式;如果只是迭代一次且关注内存效率,用生成器表达式。