当前位置: 首页 > news >正文

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 等机制的统一设计思想。

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

http://www.jsqmd.com/news/368201/

相关文章:

  • Qwen3-ASR-1.7B在Ubuntu20.04上的Docker部署全指南
  • Cogito-v1-preview-llama-3B惊艳效果展示:30种语言支持实测
  • 数字音乐解锁破解指南:解密QMC文件的技术侦探手册
  • 前端接入AI实现智能客服:技术选型与实战避坑指南
  • 鸣潮游戏帧率修复终极解决方案:WaveTools全面配置指南
  • 春联生成模型-中文-base镜像免配置:预置春节主题CSS与多终端适配样式
  • 5个专业级功能打造Obsidian代码笔记新体验
  • 知识图谱增强:Qwen3-Reranker实体链接优化方案
  • 音频解密技术指南:高效处理QMC加密文件的专业完整方案
  • Qwen3-Reranker-0.6B模型微调实战
  • 小白也能懂:DeepSeek-OCR-2核心功能全景展示
  • SDXL 1.0电影级绘图工坊:Python自动化测试框架
  • StructBERT-WebUI效果展示:电商搜索‘充电宝在哪借’精准召回‘手机没电了’语义结果
  • MusePublic Art Studio惊艳案例:基于Transformer的3D艺术生成
  • Linux命令行操作RMBG-2.0:高效批量处理技巧
  • 新手友好:PETRV2-BEV模型训练入门与实践
  • Qwen3-ForcedAligner-0.6B企业实操:无网络依赖的合规语音处理私有化部署
  • 动态LoRA自由切换:Jimeng AI Studio风格变换全解析
  • DeepSeek-OCR-2效果展示:复杂学术论文解析案例
  • Obsidian代码块美化进阶:从痛点到解决方案的完全指南
  • Nano-Banana与MySQL数据库集成实战:3D模型数据存储方案
  • MAI-UI-8B大数据处理:Hadoop集群智能管理
  • RMBG-2.0与PyCharm开发环境配置指南
  • 实时手机检测-通用模型数据库优化:从SQL到NoSQL
  • Java商城智能客服功能实现:从架构设计到性能优化
  • 突破音频加密限制:qmc-decoder全场景应用指南
  • 从安装到实战:Qwen3-ASR语音识别完整教程
  • 网络安全态势感知:BGE Reranker-v2-m3在威胁情报分析中的应用
  • StructBERT情感分类模型在母婴产品评论分析中的应用
  • HY-Motion 1.0部署案例:中小企业低成本构建3D动作生成服务