生成器的本质是实现了__iter__和__next__的状态机对象,通过挂起/恢复帧对象保存上下文,而非线程栈;yield暂停执行并返回值,下次从原位置继续,局部变量保持不变。
generator 的本质不是语法糖,而是实现了 iter 和 next 的状态机对象。
直接调用 next() 或用 for 循环驱动时,它靠保存局部变量和执行位置(yield 点)来维持上下文——这和线程栈无关,也不依赖解释器额外调度。
yield 后函数不退出?因为每次遇到 yield,Python 解释器会把当前帧(frame)挂起,把控制权交还给调用方,并把 yield 表达式的值返回。下一次调用 __next__() 时,从上次暂停的位置继续执行,局部变量全数保留。
return 在生成器中等价于 raise StopIteration,但不能带非 None 值(Python 3.3+ 允许 return value,该值会成为 StopIteration.value)yield,哪怕写了 return,也不会变成生成器——必须至少有一个 yield(哪怕在未执行的 if False: 分支里)generator 对象send() 和 throw() 怎么打破单向数据流?生成器不只是“往外吐值”,还能接收外部传入的数据或异常,从而实现协程式交互。关键在于:第一次调用 send(None) 或 next() 才启动生成器;之后才能用 send(value) 把值送进上次 yield 的位置(即 value 成为 yield 表达式的返回值)。
def echo():
while True:
received = yield
print(f"Got: {received}")
g = echo()
next(g) # 启动,停在第一个 yield
g.send("hello") # 输出 Got: hello
g.send(42) # 输出 Got: 42
next(g) 或 g.send(None),否则报 TypeError: can't send non-None value to a just-started generator
throw() 会在暂停处注入异常,常用于清理资源(如配合 try/finally)close() 会触发 GeneratorExit 异常,且禁止再调用 send() 或 next()
(x*2 for x in range(1000000)) 不会立刻计算全部元素,而 [x*2 for x in range(1000000)] 会一次性分配百万级整数对象内存。
at 0x...> ;列表推导式返回 list
for 就无声结束;列表可反复迭代len() —— 它没有长度概念,除非你手动计数或转成 list(那就失去意义了)最隐蔽的问题不是语法错,而是逻辑生命周期错位:生成器已结束却还在调用 next(),抛出 StopIteration;或在 finally 中试图访问已被清空的局部变量。
StopIteration 是正常操作,但不要忽略其 value 属性(Python 3.3+),尤其用 return expr 时itertools.islice(gen, n) 替代 
list(gen)[:n],避免提前耗尽生成器yield from subgen 不是简单展开,它会将子生成器的 send()/throw()/close() 透传,且自动处理 StopIteration 的传播print(g) 看状态——要查 g.gi_running(是否正在运行)、g.gi_frame.f_lasti(字节码偏移)这类底层属性,实际开发中几乎不用,但理解它们能避开“为什么断点没进生成器体”的困惑生成器的复杂性不在写法,而在它把执行流拆成了多个时间片段。一旦你开始用 send() 或嵌套 yield from,就必须明确每个片段的输入来源、输出去向、异常边界——这些不会报错,但会让程序在某个深夜突然卡住或跳过关键步骤。