生成器函数执行到 return 时,迭代器状态立刻变为“done”,本质是触发 stopiteration 异常而非主动结束;该异常使状态机不可逆地进入耗尽态,后续 next() 均直接抛出相同异常,不再执行剩余逻辑。

生成器函数执行到 return 时,迭代器状态立刻变为“done”,本质不是“主动结束”,而是触发了 Python 迭代协议规定的终止信号——StopIteration 异常。这个异常一抛出,迭代器的内部状态机就不可逆地进入耗尽(exhausted)状态,后续任何 next() 调用都会直接抛出相同的异常,不再执行函数体剩余逻辑。
生成器本质是状态机,不是普通函数
生成器函数被调用时,返回的是一个生成器对象(即迭代器),它封装了函数的代码、局部变量、指令指针和执行栈帧。每次 next() 调用,Python 解释器恢复该栈帧,从上次暂停处(yield 后)继续执行,直到遇到下一个 yield 或 return。
-
yield:保存当前上下文,产出值,挂起执行,状态设为“暂停” -
return(含无值或带值):清空执行上下文,标记栈帧为“已关闭”,然后抛出StopIteration - 一旦抛出
StopIteration,生成器对象的_PyGen_Throw()内部逻辑会将gi_running和gi_frame置为NULL,彻底释放执行环境
return 不是“返回值”,而是“终结指令”
在生成器中,return 的语义与普通函数完全不同:它不向调用方返回数据,而是向迭代器协议发送“流已终止”的信号。Python 3.3+ 允许 return value,但该值不会出现在 for 循环或 list(gen) 中——它只被封装进 StopIteration.value,必须显式捕获异常才能获取。
-
for循环自动拦截StopIteration并静默退出,所以return值对循环完全不可见 -
next(gen)在第三次调用时直接 raiseStopIteration("done"),而非返回字符串 - 生成器对象的
__next__()方法底层就是 C 实现的gen_iternext,它在检测到函数返回后立即调用PyErr_SetNone(PyExc_StopIteration)或PyErr_SetObject(...)
状态变为 done 的不可逆性
生成器对象有一个内部标志 gi_state(在 CPython 源码中为 GEN_CLOSED 或 GEN_CLOSING)。当 return 执行完毕,解释器将该标志置为终止态,并清空帧对象(PyFrame_Clear())。此后:
- 再次调用
next()不会重启函数,而是直接 re-raise 已缓存的StopIteration -
gen.gi_frame变为None,无法再检查或恢复执行上下文 -
gen.send()、gen.throw()、gen.close()均会立即失败(ValueError: generator already exhausted)
对比普通函数调用栈的差异
普通函数的 return 是控制流跳转,栈帧自然弹出;而生成器的 return 是协议级事件驱动:它不依赖调用栈是否为空,而是由迭代器协议强制定义“只要抛出 StopIteration,就视为迭代完成”。这种设计确保了 for、itertools、异步 async for 等所有消费迭代器的机制行为一致。
- 即使生成器函数还有未执行的代码(比如
return后跟print("never reached")),也不会运行 - 哪怕
return出现在嵌套try/finally中,finally仍会执行,但执行完立刻抛异常,状态同步变 done - 该机制与协程(
async def)共享同一套底层状态机引擎,所以return在 async generator 中行为完全一致











