为何这段Python代码会抛出StopIteration异常?
问题分析与解决
核心问题
- 每次调用
generate_nos(nos)都会创建全新的生成器实例,而非复用同一个。 nos是生成器表达式((i for i in range(10))),这类迭代器是一次性的——迭代到末尾后,无法再产生新值,再次调用next()直接抛出StopIteration。
代码执行流程拆解
- 进入while循环,第一次调用
next(generate_nos(nos)):- 新生成器开始迭代
nos,取第一个值0,返回0*2=2,不等于18,循环继续。
- 新生成器开始迭代
- 第二次循环,再次调用
next(generate_nos(nos)):- 又创建新生成器,此时
nos已被前一个生成器取走0,现在取1,返回1*2=2,仍不满足条件,循环继续。
- 又创建新生成器,此时
- 重复上述过程直到第10次循环:
- 调用
next(generate_nos(nos))时,nos已被前面9个生成器取走0-8,现在取9,返回9*2=18,满足if判断条件。
- 调用
- 执行
print(next(generate_nos(nos))):- 再次创建新生成器,但
nos已经迭代完毕(所有值都被取走),调用next()直接触发StopIteration,被外层try-except捕获,输出"StopIteration"。
- 再次创建新生成器,但
你的误解纠正
你以为此时调用print(next(generate_nos([0, 1, …, 9])))会输出0,但你的代码里传入的是生成器nos而非列表。如果传入的是列表(比如list(range(10))),每次调用generate_nos都会重新遍历列表,确实能取到0,但nos是一次性生成器,无法重复迭代。
修正方案
要实现预期逻辑(找到值为18的项后,输出生成器的下一个值),需要复用同一个生成器实例:
nos = (i for i in range(10)) def generate_nos(mynos): for i in mynos: yield i * 2 try: gen = generate_nos(nos) # 仅创建一次生成器 while True: val = next(gen) if val == 18: print(next(gen)) # 复用同一个生成器取后续值 except StopIteration: print("StopIteration")
注:由于nos的最大值是9,生成器gen在返回18(9*2)后,再调用next(gen)会触发StopIteration,最终还是会输出"StopIteration"。如果想让生成器能重复遍历,可将nos改为列表nos = list(range(10))。
内容的提问来源于stack exchange,提问作者TimeToCodeTheRoad
相关产品推荐
相关产品推荐

