Python生成器详解:用"延迟计算"让程序更聪明
一、从生活场景理解生成器
想象你走进一家拉面馆,老板有两种服务方式:
- 一次性端出100碗拉面堆在你面前
- 根据你的食用速度,吃完一碗再下一碗
这就是生成器与传统列表处理的核心区别!生成器就像第二种服务方式——按需供应,避免不必要的资源浪费。
1.1 传统列表的困境
def get_numbers(n):
result = []
for i in range(n):
result.append(i**2)
return result
# 生成1百万个数字的列表
numbers = get_numbers(1000000) # 立即占用大量内存!
当处理百万级数据时,这样的代码会导致:
- 内存暴涨(每个int对象约28字节,100万数字约28MB)
- 启动延迟(需要等待所有数据生成完成)
- 资源浪费(可能只需要前100个数据)
1.2 生成器的解决方案
def generate_numbers(n):
for i in range(n):
yield i**2 # 每次产生一个结果
# 创建生成器对象(此时不进行实际计算)
numbers_gen = generate_numbers(1000000) # 内存占用几乎为0
# 按需获取数据
for num in numbers_gen:
if num > 100:
break
print(num)
生成器的魔法在于:
- 🕒 延迟计算:调用时才开始执行
- 💾 内存友好:单次只保留当前状态
- ⏸️ 可暂停恢复:通过yield保持执行上下文
二、生成器基础:从创建到使用
2.1 两种创建方式
方式1:生成器函数
def countdown(n):
print("倒计时开始!")
while n > 0:
yield n
n -= 1
print("发射!")
# 创建生成器对象
timer = countdown(5) # 此时不会打印任何内容
# 手动触发执行
print(next(timer)) # 输出:5
print(next(timer)) # 输出:4
# ...直到抛出StopIteration异常
方式2:生成器表达式
# 传统列表推导式
squares = [x**2 for x in range(10)] # 立即生成列表
# 生成器表达式
squares_gen = (x**2 for x in range(10)) # 生成器对象
print(sum(squares_gen)) # 输出:285(此时才开始计算)
2.2 执行流程详解
以这个简单生成器为例:
def sample_gen():
print("开始执行")
yield "第一个暂停点"
print("恢复执行")
yield "第二个暂停点"
print("即将结束")