def gen(): for i in range(5): X = int((yield i) or 0) + 10 + i print("X:",X) G = gen() for a in G: print(a) G.send(77)
第一阶段:i = 0
for a in G: 隐式调用 next(G),拿到 0。0G.send(77) 唤醒生成器,77 赋给 yield。X = 77 + 10 + 0 = 87。X: 87i 变成 1,遇到 yield 1,返回 1。for 循环的循环体执行完毕,自动进入下一轮,再次隐式调用 next(G)(等价于 send(None))。None 赋给 yield,计算:X = 0 + 10 + 1 = 11。X: 11i 变成 2,遇到 yield 2,返回 2。第二阶段:i = 2
for 循环拿到刚才返回的 2。2G.send(77) 唤醒生成器,计算:X = 77 + 10 + 2 = 89。X: 89i 变成 3,遇到 yield 3,返回 3。for 循环自动进入下一轮,调用 next(G)(等价于 send(None))。X = 0 + 10 + 3 = 13。X: 13i 变成 4,遇到 yield 4,返回 4。第三阶段:i = 4
for 循环拿到刚才返回的 4。4G.send(77) 唤醒生成器,计算:X = 77 + 10 + 4 = 91。X: 91i 变成 5,range(5) 结束,生成器函数自然退出。第四阶段:报错
for 循环的循环体执行完毕,自动进入下一轮,调用 next(G)。for 循环自动捕获了 StopIteration 异常,正常退出了 for 循环。G.send(77)。send(),Python 就会无情地抛出 StopIteration。Traceback... StopIteration0 X: 87 X: 11 2 X: 89 X: 13 4 X: 91 Traceback (most recent call last): File "g:\pycode\lists.py", line 10, in <module> G.send(77) StopIteration
def gen(): # 【内层 for 循环】:属于生成器内部,负责产出数据 for i in range(5): X = int((yield i) or 0) + 10 + i print("X:", X) G = gen() # 【外层 for 循环】:属于主程序,负责消费(拉取)数据 for a in G: print(a) G.send(77) 这是一个非常敏锐的问题!你注意到了代码里有两个 for 循环,而且它们的名字都叫 i。 答案是:外层的 for a in G: 先运行,内层的 for i in range(5): 后运行。 为了让你彻底看清它们的“先后顺序”和“嵌套关系”,我们可以把这段代码加上缩进,还原它真实的逻辑层级: python 编辑 def gen(): # 【内层 for 循环】:属于生成器内部,负责产出数据 for i in range(5): X = int((yield i) or 0) + 10 + i print("X:", X) G = gen() # 【外层 for 循环】:属于主程序,负责消费(拉取)数据 for a in G: print(a) G.send(77) 它们是怎么配合运行的? 这两个 for 循环并不是“谁先跑完,谁再跑”,而是嵌套触发的关系: 外层先启动:主程序执行 for a in G:,它向生成器要第一个数据。 触发内层:生成器收到请求,内层的 for i in range(5): 才开始启动,i 变成 0,然后遇到 yield i,把 0 扔给外层,然后挂起。 外层接管:外层拿到 0,打印 0,然后执行 G.send(77)。 内层继续:send(77) 唤醒了内层,内层从 yield 的地方继续往下跑,计算并打印 X: 87。然后内层循环进入下一轮,i 变成 1,遇到 yield 1,再次把 1 扔给外层,挂起。 循环往复:外层和内层就像齿轮一样,交替咬合运行,直到内层的 range(5) 跑完。 外层 for a in G: 是消费者(在主程序中)。 内层 for i in range(5): 是生产者(在生成器函数中)。 必须是外层先伸手要数据,内层才会开始干活。