news 2026/6/16 0:21:18

Python:yield 表达式详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python:yield 表达式详解

在 Python 中,yield 并不是一种“特殊的返回语句”,也不是为惰性计算或循环结构单独设计的语法技巧。从执行模型的角度看,yield 的核心作用在于:将一次函数执行拆解为一个可暂停、可恢复、可逐步推进的执行过程,并以表达式的形式暴露这一执行边界。

理解 yield,并不是理解“生成器怎么用”,而是理解 Python 如何将执行状态对象化,并使其能够参与函数调用、迭代与控制流程之中。

一、从函数调用语义说起:普通函数是一段一次性执行过程

先从普通函数的执行语义入手:

def f(): a = 1 b = 2 return a + b

当执行:

result = f()

解释器的行为可以概括为:

1、根据 def 语句创建函数对象(该函数对象内部关联了一个代码对象)

2、调用函数时,创建一个新的帧对象,即“执行帧”

3、从函数体起始位置开始顺序执行指令

4、遇到 return 语句:

• 计算返回值

• 销毁执行帧

• 将结果返回给调用方

普通函数的执行帧是一次性的。一旦 return 被执行,该次执行过程即告结束,其执行状态不会被保留,也不可能被恢复。

因此,普通函数在语义上描述的是:从开始到结束的一次完整执行帧生命周期。

二、yield 表达式语法与执行语义定位

1、yield 是表达式,而不是语句

在 Python 3 中,yield 是一种表达式(expression),而不是语句(statement)。

yield 表达式的基本语法结构:

yield <expression>

说明:

(1)<expression> 是一个普通的 Python 表达式;该表达式在执行过程中被求值,其结果作为本次迭代中产出的值交还给调用方。

(2)yield <expression> 的语义作用在于:在执行过程中产出一个值,并暂停当前执行帧。

当 <expression> 被省略时:

yield

其语义等价于:

yield None

即产出值为 None,但暂停执行的语义保持不变。

2、yield 对函数调用语义的根本影响

一旦函数体中出现 yield 表达式,解释器在语法分析阶段就会将该函数判定为生成器函数。这一判定结果会直接影响该函数在调用阶段的语义:

• 调用普通函数 → 立即创建执行帧并执行函数体

• 调用生成器函数 → 返回一个生成器对象,而不立即执行函数体

也就是说,yield 改变的并不是函数体中的某一步行为,而是从定义层面改变了函数调用的执行模型与返回结果类型。

从语言设计角度看,yield 的引入并非为了简化循环或节省内存,而是为了允许:函数的执行过程本身成为一个可被拆解、可被对象化、可被外部逐步驱动的运行期实体。

3、普通函数与生成器函数:定义层与执行层的差异

从定义层面看,Python 中的函数可以分为两类:

• 普通函数:函数体中不包含 yield

• 生成器函数:函数体中至少包含一条 yield 表达式

这一差异在 def 语句执行时即已确定,并体现在函数对象所关联的代码对象上。

两类函数在调用语义上的根本区别在于:

• 调用普通函数时,解释器立即创建执行帧并执行代码

• 调用生成器函数时,解释器返回一个生成器对象,而暂不创建执行帧

生成器对象可以被理解为对生成器函数一次潜在执行过程的封装。

它本身并不等同于执行帧,而是一个运行期控制对象,用于在需要时创建、保存并恢复执行帧。

三、yield 的核心语义:冻结当前执行帧

对于包含 yield 的函数而言,def 语句仅创建函数对象,调用该函数时才返回生成器对象。

执行帧的创建与激活,则发生在生成器对象被首次推进时(如 next() 或 send(None))。

示例:

def g(): x = 1 yield x x += 1 yield x + 1

当执行:

gen = g()next(gen)

解释器的执行过程为:

1、调用 g(),返回生成器对象

2、调用 next(gen):

→ 创建并激活执行帧

→ 从函数体起始位置开始执行

3、执行至 yield x:

→ 对表达式 x 求值

→ 将该值交还给调用方

→ 冻结当前执行帧的状态(包括指令位置、局部变量绑定及相关执行上下文)

这里所谓的“冻结”,指的是执行帧的暂停。即,函数的执行过程尚未完成,其执行状态被完整保留下来,等待后续恢复。

四、恢复执行与执行终止:yield 与 return 的语义分野

当生成器对象被多次推进时,解释器不会重新调用生成器函数,也不会重新创建执行帧。

gen = g()print(next(gen)) # 1print(next(gen)) # 3print(next(gen)) # StopIteration

执行过程是:

1、第一次 next(gen):

→ 创建并激活执行帧

→ 执行至第一个 yield,返回 yield 表达式的值

→ 然后冻结执行帧

2、第二次 next(gen):

→ 恢复此前冻结的执行帧,从上一次 yield 之后继续执行

→ 执行至第二个 yield,返回此 yield 表达式的值

3、第三次 next(gen):

→ 恢复冻结的执行帧,从第二次 yield 之后继续执行

→ 已到生成器函数底部,无法继续推进,抛出 StopIteration,销毁执行帧

整个过程中,推进的始终是同一个执行帧。

由此可以清晰地区分 yield 与 return 的本质差异:

(1)yield 表示执行过程的暂停点

• 执行帧被保留

• 执行状态可恢复

(2)return 表示执行过程的终止点

• 执行帧被销毁

• 执行过程不可恢复

当生成器函数运行至末尾,或在生成器函数内显式执行 return 时:

• 解释器向调用方抛出 StopIteration

• 执行帧被销毁

• 生成器对象进入终止态

因此,两者的区别并不在于是否“返回值”,而在于是否保留并允许继续推进当前执行帧。

五、yield 作为表达式:值的双向流动与 send 机制

绝大多数 Python 表达式的值,均在其执行当下立即确定。但 yield 是一个例外。

yield 表达式的产出值在执行当下确定,而该表达式的求值结果则延迟到下一次恢复执行时,由外部注入。

示例:

def gen(): x = yield 1 yield x

当执行:

g = gen()next(g) # 产出 1g.send("hello") # 将 "hello" 注入

g.send("hello") 并不是向生成器“整体”发送一个值,而是将 "hello" 作为上一次暂停点处的 yield 1 这个表达式的求值结果,然后将该值绑定给变量 x。

可以这样说,第一次 next(g) 大致等价于:

g.send(None)

也就是说,首次可使用 send() 向生成器对象发送 None 值来创建并激活执行帧。

不过,要注意的是,在生成器尚未启动之前,不能直接使用 send() 向生成器发送非 None 值,解释器将抛出 TypeError。原因是:

• 尚未创建执行帧

• 尚不存在暂停点

因而不存在可接收值的 yield 表达式。

六、yield from 的基本语义概览

yield from 是一种用于委托执行控制权的表达式。它在一段时间内暂停当前生成器的执行,并将执行推进、值注入、异常处理与终止请求统一转交给被委托对象;在被委托对象完成后,再恢复自身执行并接收其完成态返回值。

请参阅:

《Python:yield from 表达式详解》

📘 小结

yield 是一种用于冻结执行帧的表达式。它并不返回生成器对象,而是在生成器执行过程中产出值并暂停当前执行帧,从而将一次函数执行拆解为多个可恢复的执行阶段。作为表达式,yield 还支持在恢复执行时接收外部注入的值。

理解 yield 的语义,有助于从执行模型层面把握生成器、send 与 yield from 等机制的统一设计思想。

“点赞有美意,赞赏是鼓励”

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/14 19:29:59

Meixiong Niannian画图引擎与PID控制结合:智能绘画过程优化

Meixiong Niannian画图引擎与PID控制结合&#xff1a;智能绘画过程优化 1. 引言&#xff1a;当AI绘画遇上智能控制 你有没有遇到过这样的情况&#xff1a;用AI画图工具生成图片时&#xff0c;效果时好时坏&#xff0c;参数调来调去就是达不到理想的效果&#xff1f;或者生成过…

作者头像 李华
网站建设 2026/6/10 15:17:23

YOLO12在GitHub上的开源项目实战

YOLO12在GitHub上的开源项目实战 最近在GitHub上闲逛&#xff0c;发现YOLO12的开源项目热度挺高。作为YOLO系列的最新成员&#xff0c;它这次玩了个大的——直接把注意力机制&#xff08;Attention&#xff09;塞进了实时目标检测框架里。说实话&#xff0c;第一次看到这个思路…

作者头像 李华
网站建设 2026/6/15 7:48:32

Qwen2.5-Coder-1.5B代码优化案例:提升算法执行效率300%

Qwen2.5-Coder-1.5B代码优化案例&#xff1a;提升算法执行效率300% 最近在折腾一些数据处理脚本&#xff0c;发现一个老旧的排序算法在处理稍大一点的数据集时&#xff0c;慢得让人想砸键盘。原本想着手动优化&#xff0c;但转念一想&#xff0c;这不正好试试新出的Qwen2.5-Co…

作者头像 李华
网站建设 2026/6/10 16:42:33

DeepSeek-OCR 2 体验:上传图片自动识别表格和文字

DeepSeek-OCR 2 体验&#xff1a;上传图片自动识别表格和文字 最近在整理一些纸质文档和扫描件时&#xff0c;遇到了一个头疼的问题&#xff1a;大量的表格和文字内容需要手动录入电脑。这活儿不仅枯燥&#xff0c;还特别容易出错。就在我准备硬着头皮开始这项“体力活”时&am…

作者头像 李华
网站建设 2026/6/10 20:33:59

Python入门:使用灵毓秀-牧神-造相Z-Turbo生成第一个AI作品

Python入门&#xff1a;使用灵毓秀-牧神-造相Z-Turbo生成第一个AI作品 本文面向Python初学者&#xff0c;手把手教你如何调用AI绘画API&#xff0c;快速生成你的第一个AI作品。无需深厚的技术背景&#xff0c;跟着步骤操作即可体验AI创作的乐趣。 1. 环境准备&#xff1a;安装必…

作者头像 李华