想象你正在观看一部可以随时暂停的科幻电影:
def time_machine():
yield "回到1920年"
yield "穿越到2050年"
yield "抵达恐龙时代"
traveler = time_machine()
print(next(traveler)) # 回到1920年
print(next(traveler)) # 穿越到2050年
这不是普通的函数,而是一台可以随时暂停的时间机器。今天我们将揭开Python最迷人的特性——生成器与协程的神秘面纱。
一、生成器:会呼吸的函数
普通函数像自动售货机(投币→出货),生成器像咖啡师(边做边交流):
# 传统函数
def make_coffee():
return "☕"
# 生成器函数
def barista():
yield "磨豆"
yield "萃取"
yield "拉花"
yield "☕"
order = barista()
print(next(order)) # 磨豆
print(next(order)) # 萃取
生成器三大特征:
-
用
yield
替代return
-
执行到yield时冻结现场
-
通过
__next__()
唤醒执行
二、惰性求值的艺术
对比两种实现方式处理1000万数据:
# 普通列表(立即加载)
def load_all():
return [i**2 for i in range(10_000_000)] # 瞬间吃掉400MB内存
# 生成器(按需生产)
def stream_data():
for i in range(10_000_000):
yield i**2 # 始终只占几十字节
内存占用实测对比:
方式 | 内存峰值 | 启动速度 | 适用场景 |
---|---|---|---|
列表 | 400MB | 慢 | 小数据集即时处理 |
生成器 | 50KB | 即时 | 大数据流处理 |
三、协程:可以对话的生成器
用.send()
方法实现双向通信:
def smart_thermostat():
current_temp = 25
while True:
new_temp = yield current_temp
if new_temp is not None:
current_temp = new_temp
ac = smart_thermostat()
next(ac) # 启动协程
print(ac.send(20)) # 20
print(ac.send(None)) # 保持20
协程进化史:
-
Python 2.5: 基本协程
-
Python 3.3: yield from
-
Python 3.5: async/await
四、生成器表达式:隐形的内存杀手锏
两种写法看似相同,实则天差地别:
# 列表推导式(立即生成)
big_list = [x*2 for x in range(10**6)] # 占用8MB内存
# 生成器表达式(惰性计算)
gen_expr = (x*2 for x in range(10**6)) # 内存几乎为0
# 管道式处理
pipeline = (x**2 for x in gen_expr if x%3 ==0)
性能对比测试(处理1GB数据):
import time
# 传统方式
start = time.time()
[x**2 for x in range(10**7) if x%2 ==0]
print(f"列表耗时: {time.time()-start:.2f}s")
# 生成器方式
start = time.time()
sum(x**2 for x in range(10**7) if x%2 ==0)
print(f"生成器耗时: {time.time()-start:.2f}s")
典型结果:
-
列表方式:3.21秒(内存爆满)
-
生成器方式:2.87秒(内存平稳)
五、实战中的时空穿梭
大文件流式处理:
def read_giant_file(path):
with open(path, 'r') as f:
while chunk := f.read(4096):
yield chunk
# 处理10GB文件只需40MB内存
for block in read_giant_file("huge.log"):
process(block)
无限序列生成:
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# 获取前100个斐波那契数
print([x for _,x in zip(range(100), fibonacci())])
状态机实现:
def traffic_light():
states = ["🔴", "🟡", "🟢"]
index = 0
while True:
yield states[index]
index = (index + 1) % 3
yield states[index]
index = (index + 1) % 3
# 交替闪烁黄灯
light = traffic_light()
print(next(light)) # 🔴
print(next(light)) # 🟡
print(next(light)) # 🟢
print(next(light)) # 🟡
当你理解生成器的本质后,会看到Python代码中处处流淌着时间的长河。每个yield都是一个时间锚点,协程是跨越时空的对话,而async/await则是操控时间的终极咒语。