什么是Python中的生成器?
在 Python 中,生成器(Generator) 是一种特殊的迭代器(Iterator)。
简单来说,生成器允许你定义一个可以像列表一样进行遍历(迭代)的函数,但它不会一次性把所有数据都加载到内存中,而是用一个生成一个(即“惰性求值”)。
以下是关于生成器的详细解释:
1. 核心概念:为什么要用生成器?
想象你需要处理 1 亿个数字:
- 列表(List): Python 会立刻在内存中创建这 1 亿个数字。这会占用巨大的内存,甚至导致程序崩溃。
- 生成器(Generator): Python 不会立刻创建数字。它只是记住了生成的规则。当你需要第 1 个数字时,它算出来给你;当你需要第 2 个时,它再算第 2 个。它几乎不占内存。
2. 如何创建生成器?
有两种主要方式来创建生成器:
方法 A:生成器函数(使用 yield 关键字)
这是最常见的方法。如果一个函数中包含了 yield 关键字,那么这个函数就变成了一个生成器函数。
return:函数执行完毕,返回结果,销毁局部变量。yield:函数暂停执行,返回一个值,但保留当前的局部变量和执行状态。下次调用时,从暂停的地方继续执行。
python
def simple_generator():
print("开始执行...")
yield 1 # 暂停,返回 1
print("回来继续执行...")
yield 2 # 暂停,返回 2
print("最后一次执行...")
yield 3 # 暂停,返回 3
# 调用函数不会立即执行代码,而是返回一个生成器对象
gen = simple_generator()
# 使用 next() 获取值
print(next(gen)) # 输出: 开始执行... \n 1
print(next(gen)) # 输出: 回来继续执行... \n 2
print(next(gen)) # 输出: 最后一次执行... \n 3
# print(next(gen)) # 再次调用会抛出 StopIteration 异常,表示没有数据了
方法 B:生成器表达式
类似于列表推导式(List Comprehension),但是用圆括号 () 代替方括号 []。
python
# 列表推导式(立即生成所有数据,占内存)
my_list = [x * x for x in range(10)]
# 生成器表达式(不立即生成,几乎不占内存)
my_gen = (x * x for x in range(10))
print(my_gen) # 输出: <generator object ...>
print(next(my_gen)) # 输出: 0
print(next(my_gen)) # 输出: 1
3. 如何使用生成器?
通常我们不会手动调用 next(),而是直接在 for 循环中使用它。for 循环会自动处理 StopIteration 异常。
python
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
# 像使用列表一样使用生成器
for num in fibonacci(5):
print(num)
4. 生成器的优缺点
优点:
- 节省内存(Memory Efficient): 这是最大的优势。处理大数据文件或无限数据流时非常有用。
- 惰性求值(Lazy Evaluation): 只有在需要值的时候才计算,避免了不必要的计算。
- 可以表示无限流: 因为不需要一次性存储,你可以写一个永远产生数据的生成器(例如无限斐波那契数列),只要你不停止请求,它就一直生成。
缺点:
- 只能遍历一次: 生成器就像水流,流过去就没了。如果你想再次遍历,必须重新创建一个新的生成器对象。
- 无法通过索引访问: 你不能写
gen[5],因为第 5 个元素还没算出来呢,你必须先算出前 4 个。 - 无法获取长度: 不能使用
len(gen)。
5. 总结对比
| 特性 | 列表 (List) | 生成器 (Generator) |
|---|---|---|
| 创建方式 | [x for x in range(10)] |
(x for x in range(10)) |
| 内存占用 | 高(存储所有数据) | 极低(只存储状态) |
| 计算方式 | 立即计算所有结果 | 需要时才计算 (Lazy) |
| 访问方式 | 索引访问 list[0],可多次遍历 |
只能 next() 或迭代,只能遍历一次 |
| 适用场景 | 数据量小,需要频繁随机访问 | 数据量大,只需要顺序读取 |
举个通俗的例子
- 列表就像是买了一整箱做好的饼干。你需要找地方放这箱饼干(占内存),不管你吃不吃,钱都花了(已计算)。
- 生成器就像是请了一位厨师。你饿了说“给我一块饼干”,厨师现场做一块给你。你不吃,厨师就站着不动。不需要仓库放饼干,但每次吃都需要一点时间现做。