写 Python 的时候,你可能听过“生成器表达式”这个词,它看起来有点像列表推导式,但本质不太一样。本文就来带你从头到尾搞懂生成器表达式:它是什么、怎么用、有什么优势,还有常见的使用场景。
参考文章: Python 迭代器 | 简单一点学习 easyeasy.me
目录
- 什么是生成器表达式?
- 和列表推导式有什么区别?
- 生成器表达式的基本语法
- 如何从生成器表达式中取值?
- for 循环 + 生成器表达式
- 与内置函数组合使用(sum / max / any / all 等)
- 用生成器表达式节省内存
- 多层嵌套生成器表达式
- 生成器表达式的注意事项
- 小结与建议
1. 什么是生成器表达式?
生成器表达式(Generator Expression)是 Python 提供的一种快速创建生成器的方式,语法很像列表推导式,但不会一次性生成所有数据,而是每次只生成一个值。
通俗讲:
它就像是个“懒人版”的列表,只在需要的时候给你值,节省内存。
2. 和列表推导式有什么区别?
来看一个例子直观感受下:
# 列表推导式:马上生成完整列表
squares_list = [x * x for x in range(5)]
# 生成器表达式:不会立刻计算,返回一个生成器对象
squares_gen = (x * x for x in range(5))
输出:
print(squares_list) # [0, 1, 4, 9, 16]
print(squares_gen) # <generator object ...>
✅ 列表推导式:快但占内存
✅ 生成器表达式:省内存但“懒惰”
3. 生成器表达式的基本语法
语法几乎和列表推导式一样,只不过把 [] 换成了 ():
(expression for item in iterable if condition)
例子:
gen = (x * 2 for x in range(5) if x % 2 == 0)
print(next(gen)) # 0
print(next(gen)) # 4
print(next(gen)) # 8
4. 如何从生成器表达式中取值?
生成器表达式的值是“一个个生成”的,可以用以下几种方式获取:
方法1:next()
g = (i for i in range(3))
print(next(g)) # 0
print(next(g)) # 1
print(next(g)) # 2
# print(next(g)) # StopIteration 错误
方法2:for 循环
for i in (x * 3 for x in range(4)):
print(i)
输出:
0
3
6
9
5. for 循环 + 生成器表达式
你可以直接把生成器表达式嵌在 for 循环里:
for name in (s.strip() for s in [" Alice ", " Bob ", "Charlie "]):
print(name)
这样写代码简洁又高效。
6. 与内置函数组合使用(sum / max / any / all 等)
生成器表达式非常适合跟一些内置函数组合使用,避免中间生成临时列表:
# 计算总和
total = sum(x for x in range(1000000))
# 找最大值
m = max(x for x in [3, 7, 2, 9])
# 判断是否有偶数
has_even = any(x % 2 == 0 for x in range(10))
# 判断是否所有数字都小于 100
all_small = all(x < 100 for x in range(50))
这样写,比先用列表推导式再传进去更节省资源。
7. 用生成器表达式节省内存
列表推导式在数据量很大时可能会炸内存,比如:
# 列表推导式会先生成一个完整列表,占用大量内存
big_list = [x for x in range(10**8)]
改成生成器表达式就安全多了:
big_gen = (x for x in range(10**8))
它不会一次性占内存,只会在你用 next() 或 for 的时候才生成下一个值。
8. 多层嵌套生成器表达式
如果你想搞点复杂的嵌套,也没问题:
g = ((x, y) for x in range(3) for y in range(2))
for pair in g:
print(pair)
输出:
(0, 0)
(0, 1)
(1, 0)
(1, 1)
(2, 0)
(2, 1)
注意:虽然能写,但太复杂时可读性不太好,适可而止哈。
9. 生成器表达式的注意事项
- 只能遍历一次:生成器用过就没了,不能像列表那样重复用
- 不能直接索引:不能用
gen[0],因为它不是列表 - 不能回头:一旦走过了某个值,不能退回去,只能重建一个新的生成器
例子:
g = (x for x in range(3))
list(g) # [0, 1, 2]
list(g) # [],因为前面已经用完了
10. 小结与建议
| 特性 | 列表推导式 | 生成器表达式 |
|---|---|---|
| 返回类型 | list | generator |
| 内存使用 | 高(一次性全部生成) | 低(按需生成) |
| 速度 | 快 | 慢一点(因为惰性) |
| 可迭代次数 | 无限次 | 一次(用完即焚) |
| 支持索引访问 | 是 | 否 |
建议使用时机:
- 如果你只需要一次遍历、数据量很大、对速度要求不高 —— 用生成器表达式
- 如果你要多次用这个结果、要随机访问 —— 用列表推导式