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

《流畅的Python》读书笔记19(补充01): 使用 yield from - 再谈PE380

PEP 380: Syntax for Delegating to a Subgenerator是 Python 发展史上一个里程碑式的提案,它在Python 3.3中正式引入。

核心语法是:yield from <iterable>

以下从背景痛点、架构原理、使用方法和注意事项四个维度详细解析。


1. 为什么需要引入?(解决什么痛点)

在 PEP 380 之前,如果你想在一个生成器(主生成器)中调用另一个生成器(子生成器),你需要手动处理所有交互细节。这被称为“生成器委托”的缺失。

痛点分析:
  1. 代码冗余与复杂:必须写一个for循环来遍历子生成器。
  2. 双向通信断裂:如果外部调用者使用.send(value)发送数据,或者.throw(exc)抛出异常,手动的for循环无法将这些操作透明地转发给子生成器。
  3. 返回值丢失:子生成器可以通过return value返回一个值(在 Python 3.3+ 中支持),但手动循环很难优雅地捕获这个返回值并传递给主生成器。
  4. 性能损耗:Python 层的for循环比 C 层实现的yield from慢。
解决的问题:
  • 透明代理:让主生成器像一个“透明管道”,外部调用者感觉直接在与子生成器交互。
  • 简化重构:可以将大型生成器拆分为多个小的子生成器,而无需改变外部调用逻辑。
  • 为协程奠基:它是async/await的前身,使得基于生成器的协程组合成为可能。

2. 架构与流程图解

A. 架构图:三方关系

PEP 380 自动处理区域 (透明委托)

send / next / throw

yield from

自动转发 send/throw

自动 yield 值

return 值

最终结果 / StopIteration

外部调用者
(Caller)

主生成器
(Main Generator)

子生成器
(Sub-generator)

关键点yield from建立了一个双向通道。MainGen不再仅仅是产生值,它成为了SubGenCaller之间的代理。

B. 执行流程图
Sub Gen (子)Main Gen (主)Caller (外部)Sub Gen (子)Main Gen (主)Caller (外部)初始化阶段运行阶段 (循环)loop[直到子生成器结束]结束阶段g = main_gen()next(g) / g.send(None)启动 sub_gen()yield value产出 valuesend(new_data) 或 throw(exc)转发 new_data 或 exc处理并继续 yield 或 抛出异常return result_value (触发 StopIteration)捕获 result_value抛出 StopIteration (携带 result_value)

3. 如何使用?

基本语法
result=yieldfromsub_generator()
场景一:简单委托(替代 for 循环)

旧写法 (Python < 3.3):

defchain(*iterables):foritiniterables:foriteminit:yielditem

新写法 (PEP 380):

defchain(*iterables):foritiniterables:yieldfromit

注:这里it可以是任何可迭代对象,不仅仅是生成器。

场景二:双向通信与返回值(核心威力)

这是yield from真正发挥作用的地方。

defsub_coroutine():"""子协程:接收数据,累加,最后返回总和"""total=0whileTrue:try:# 接收外部发送的值value=yieldtotalifvalueisNone:breaktotal+=valueexceptGeneratorExit:print("Sub: Cleaning up")breakreturntotal# Python 3.3+ 允许生成器返回值defmain_coroutine():"""主协程:委托给子协程"""print("Main: Starting sub-coroutine")# yield from 表达式的值就是 sub_coroutine 的 return 值result=yieldfromsub_coroutine()print(f"Main: Sub-coroutine returned{result}")returnresult# --- 测试代码 ---importsys coro=main_coroutine()next(coro)# 启动主协程,进而启动子协程# 发送数据,这些数据会透明地穿过 main_coroutine 到达 sub_coroutineprint(coro.send(10))# 输出: 10 (sub_coroutine 当前的 total)print(coro.send(20))# 输出: 30print(coro.send(30))# 输出: 60try:coro.send(None)# 发送 None 触发 sub_coroutine 的 break,进而 returnexceptStopIterationase:print(f"Final Result:{e.value}")# 输出: Final Result: 60

解析:

  1. main_coroutine中的yield from sub_coroutine()暂停了主协程,将控制权交给子协程。
  2. coro.send(10)发送给main_coroutine,但yield from自动将其转发给sub_coroutine
  3. sub_coroutineyield出的值,直接通过main_coroutine返回给外部调用者。
  4. sub_coroutinereturn total时,触发StopIteration,其value属性被赋值给main_coroutine中的result变量。

4. 需要注意什么问题?

1.StopIteration的处理
  • 当子生成器结束时,它会抛出StopIteration
  • yield from捕获这个异常,并从异常对象中提取value属性作为yield from表达式的返回值。
  • 注意:如果子生成器没有return值,yield from的结果是None
2. 异常传播
  • 如果子生成器抛出非StopIteration的异常,该异常会直接穿透yield from,抛给主生成器的调用者。
  • 如果外部调用者对主生成器调用.throw(exc),该异常会被转发给子生成器。如果子生成器没有处理该异常,它会向上传播。
3.GeneratorExit的处理
  • 当主生成器被垃圾回收或显式关闭(.close())时,GeneratorExit异常会被抛出。
  • yield from确保这个异常也被转发给子生成器,以便子生成器有机会进行清理工作(如关闭文件、网络连接等)。
4. 可迭代对象 vs 生成器
  • yield from后面可以跟任何可迭代对象(列表、元组、字符串等),不仅仅必须是生成器。
  • 但如果要跟的是普通可迭代对象,它只是简单地yield每个元素,不涉及.send()/.throw()的双向通信机制。只有当右边是一个生成器/协程时,双向通信才生效。
5. 与async/await的关系
  • 在 Python 3.5 引入async/await后,yield from在协程中的地位被await取代。
  • 最佳实践
    • 如果你写的是异步代码(async def),请使用await
    • 如果你写的是同步生成器(def+yield),并且需要委托给另一个生成器,请继续使用yield from
    • 不要在async def中使用yield from(虽然早期 asyncio 兼容,但现在已不推荐)。

总结

PEP 380 的yield from是 Python 生成器机制的“胶水”,它解决了生成器组合时的复杂性,实现了透明的双向通信和返回值传递。它是理解 Python 协程演进(从 generator-based coroutines 到 native coroutines)的关键一环。

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

相关文章:

  • Claude插件报错急救指南:从诊断到修复的完整手册
  • nodejs nvm 安装与使用教程
  • Sora 2新闻视频生成全链路拆解(含CNN/BBC已验证的伦理校验模板与版权规避清单)
  • 西安企来客科技深度调查:西北 GEO 黑马崛起真相揭示
  • 2026年北京通骋公司靠谱排名 - 工业品牌热点
  • 微服务中集成大模型调用的降级限流与优雅容灾实践
  • Sora 2视频画质突变真相:3大压缩伪影、2类运动失真、5种光照崩溃场景全曝光(工程师内部测试日志)
  • 1J33选购攻略 - mypinpai
  • 别再手动改乱码了!用convmv命令一键搞定Linux下GBK到UTF-8的文件夹编码转换
  • 【工信部信通院认证】Sora 2虚拟主播视频生成合规性自测工具包(含17项AI内容水印检测项)
  • 别再用OBS了!Sora 2原生录制引擎对比测试:延迟降低63%,带宽节省41%,但90%用户忽略的License授权陷阱
  • 2026成都训犬寄养机构评测:魔宠犬堡联系与服务解析 - 优质品牌商家
  • 新国标甲级防火门和乙级防火门有什么区别
  • Sora 2视频增强不再依赖GPU堆砌:轻量化推理方案实测提速3.8倍,单卡3090实时处理1080p@24fps
  • 如何用ShawzinBot将MIDI音乐变成Warframe游戏内演奏:新手完整指南
  • 在Ubuntu 20.04上从源码编译Wayland全家桶(Weston 10.0.3),我踩过的坑你别再踩了
  • 如何用WaveTools鸣潮工具箱彻底改变你的游戏体验:终极优化指南
  • 平面设计师最后的护城河:Sora 2时代必须掌握的3类不可替代性动画思维(附客户提案话术库)
  • 【Sora 2用户体验深度解密】:20年AI影像架构师亲测的5大颠覆性交互升级与3个未公开优化细节
  • Sora 2交互原型实测全记录:3小时复现官方演示级动效,附可运行Figma组件库(限前200名领取)
  • Win10/Win11下Cadence Allegro与OrCAD联动卡顿?你的操作习惯可能是元凶
  • Paperxie 课程论文写作功能实测:期末周从熬夜肝稿到一键生成的效率革命
  • 【孤岛划分】分布式能源接入弹性配电网模型研究【IEEE33节点】(Matlab代码实现)
  • 2026年近期安徽铜陵代理记账公司深度分析与选择指南 - 2026年企业资讯
  • 甲级防火门标准规格与选购指南
  • 2026年6月北京宣传片拍摄公司推荐:五大口碑榜夜读防疲劳评测专业价格 - 品牌推荐
  • CentOS 7下解决‘devtoolset-9-gcc-c++’找不到的完整流程(附repo文件缺失排查)
  • Jellyfin Android TV客户端:打造智能电视媒体中心的终极解决方案
  • 简化 Hermes 部署流程 Windows 专属整合包使用教程|5分钟轻松搭建
  • Windows更新后C盘莫名少了10个G?一文讲透windows.old是什么、该不该删、以及最安全的删除姿势