本文目录导读:
我们来通过一个简单的案例,彻底搞懂 Python 生成器。
生成器的核心思想是惰性求值(Lazy Evaluation):它不一次性生成所有数据,而是在你需要的时候才一个接一个地“生产”出来,这能极大地节省内存。
核心概念
yield关键字:生成器函数里用yield代替return。- 状态保留:每次调用
next()时,函数从上次yield退出的地方继续执行。 next()函数:手动获取下一个值。for循环:自动获取所有值(更常用)。
案例1:最简单的生成器
对比生成列表和生成器,看内存差异。
# 1. 普通函数:返回一个列表(一次性创建所有数据)
def get_squares_list(n):
squares = []
for i in range(n):
squares.append(i * i)
return squares
# 2. 生成器函数:每次yield一个值
def get_squares_generator(n):
for i in range(n):
yield i * i # 每次生成一个,暂停在这里
# --- 使用对比 ---
print("普通列表:")
list_result = get_squares_list(5) # 立即创建 [0, 1, 4, 9, 16]
print(list_result) # 输出完整列表
print("\n生成器:")
gen_result = get_squares_generator(5) # 创建了一个生成器对象,还没有计算任何值!
print(gen_result) # 输出:<generator object ...>
# 用for循环逐个取出来(自动调next)
for value in gen_result:
print(value, end=' ') # 输出:0 1 4 9 16
案例2:经典斐波那契数列
对比普通函数和生成器的内存消耗。
假设要“生成第1亿个斐波那契数后的100个数”——普通列表会炸内存,生成器毫发无伤。
# 生成器方式:每次只记得前两个数,不保留整个序列
def fibonacci_generator(limit):
a, b = 0, 1
count = 0
while count < limit:
yield a # 返回当前值,暂停
a, b = b, a + b
count += 1
print("斐波那契数列前10项:")
for num in fibonacci_generator(10):
print(num, end=' ') # 0 1 1 2 3 5 8 13 21 34
# 如果极限值很大(比如10^8),普通列表会崩溃,但生成器只占用极少内存
案例3:用 next() 手动控制
有时候你不想一次全取完,可以“走一步看一步”。
def countdown(start):
while start > 0:
yield start
start -= 1
yield "Go!"
gen = countdown(3)
print("手动调用next:")
print(next(gen)) # 3
print(next(gen)) # 2
print(next(gen)) # 1
print(next(gen)) # Go!
# 如果再调一次 next(gen),会触发 StopIteration 异常(for循环会自动处理)
为什么能“省内存”?—— 本质对比
| 特性 | 普通列表(return [...]) |
生成器(yield) |
|---|---|---|
| 数据存储 | 全部在内存中 | 一次只存一个值 |
| 读取方式 | 可以索引、切片、反复使用 | 只能单向遍历,用后即忘 |
| 适用场景 | 数据量小,需要随机访问 | 数据量大,只需顺序处理 |
| 性能 | 创建快,但不适合超大序列 | 创建极快,几乎不占额外内存 |
关键理解
- 生成器不是“一次性计算所有结果的函数”,而是可暂停的函数。
- 每次
yield就像函数对你说:“给你一个值,我先歇会儿,下次你再叫我,我接着干。” - 你用
for循环时,Python 自动不断地next()它,直到没有yield了。
需要处理海量数据但又不想让内存爆炸时,用生成器,它是“按需生产”的懒人神器。
你可以把上面代码复制到 Python 里跑一遍,重点看生成器 print(gen_result) 的输出——它只是一个对象,没有数字,再对比 list_result 已经占了一大块内存,这就是区别。
标签: Python