You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用yield from共享生成器时,为何看似等价的代码会出现StopIteration异常差异?

yield from共享生成器时,为何看似等价的代码会出现StopIteration异常差异?

嘿,这个问题的核心其实藏在Python生成器的一个关键特性里——生成器是有状态的,而且一旦被迭代,它的状态会持续保留,不会自动重置。再加上你这里所有的func生成器都绑定了同一个全局gen对象,这两种写法的差异就由此而来了。

咱们逐个拆解:

先看案例1:保存生成器对象复用

gen = (x for x in range(10))
def func():
    yield from gen

F21 = func()
F22 = func()
next(F21)  # 输出0,gen的内部指针推进到下一个元素
next(F22)  # 输出1,gen的指针继续向前

这里的F21和F22是两个独立的生成器对象,但它们共享同一个gen生成器的状态。当你调用next(F21)时,yield from会委托gen产出第一个元素(0),此时gen的内部状态已经变了——它现在指向的是下一个要产出的元素1;之后调用next(F22),它同样会委托这个已经被推进过的gen,自然就拿到了1。只要gen还没耗尽(也就是还没产出完10个元素),不管你用F21还是F22调用next,都能拿到下一个元素,不会提前触发StopIteration。

再看案例2:每次创建新生成器

gen = (x for x in range(10))
def func():
    yield from gen

next(func())  # 输出0,gen指针推进到1
next(func())  # 正常应该输出1,除非gen已经被耗尽

你说这个案例会抛出StopIteration,其实正常情况下前10次调用next(func())都不会有问题——每次调用func()都会生成一个新的生成器对象,但这个新对象依然会绑定那个全局的gen,所以每次next都会推进gen的状态,依次产出0、1、2...直到第10次产出9。第11次调用next(func())时,gen已经没有元素可以产出了,才会抛出StopIteration。

那为什么你觉得两个案例有差异?大概率是你误以为案例2中每次调用func()都会“重置”迭代,从头开始产出0,但实际上因为gen是一个已创建的生成器对象(不是每次调用都生成新生成器的函数),所有func生成器都共享它的状态,所以迭代是连续的。

反过来,如果把gen改成一个返回新生成器的函数,比如:

def gen():
    yield from range(10)
def func():
    yield from gen()

这时候案例2每次调用next(func())都会从头产出0,永远不会提前抛出StopIteration(除非你调用超过10次同一个生成器对象),但这就是另一种场景了。

总结一下:

  • 生成器对象的状态是全局共享的,一旦被迭代就会持续推进;
  • 案例1和案例2的本质差异不是保存不保存生成器对象,而是你是否理解所有func生成器都在复用同一个gen的状态;
  • 只有当gen被完全耗尽(产出完所有10个元素)时,后续的next才会触发StopIteration。

备注:内容来源于stack exchange,提问作者Hawii Hawii

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.21 12:50:29