news 2026/4/15 19:14:54

2025-简单点-python中的生成器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
2025-简单点-python中的生成器

生成器:惰性求值与协程基础

生成器让你能够按需产生值,而不是一次性在内存中构建整个序列,这对于处理大型或无限的数据流至关重要 。

生成器函数与yield

任何包含yield关键字的函数都是一个生成器函数。调用它时,会返回一个生成器对象。

defcountdown(n):print("Starting countdown from",n)whilen>0:yieldn# 每次执行到yield时暂停,返回n的值n-=1# 创建生成器对象counter=countdown(3)print(next(counter))# 输出: Starting countdown from 3 \n 3print(next(counter))# 输出: 2print(next(counter))# 输出: 1# print(next(counter)) # 再调用会抛出StopIteration异常

生成器表达式

类似于列表推导,但使用圆括号,并且返回一个生成器 。

# 列表推导:立即生成所有数据,占用内存list_comp=[x*xforxinrange(1000000)]# 生成器表达式:按需生成数据,节省内存gen_exp=(x*xforxinrange(1000000))

协程:生成器的双向通信

生成器不仅可以产出值,还可以通过.send(value)方法接收值,这使其成为协程的基础 。

defaverager():total=0.0count=0whileTrue:new_value=yieldtotal# yield表达式可以接收外部send进来的值ifnew_valueisNone:# 通常用None作为哨兵值来终止协程breaktotal+=new_value count+=1ifcount>0:total=total/count# 计算平均值avg_cor=averager()next(avg_cor)# 预激(prime)协程,使其运行到第一个yield处暂停print(avg_cor.send(10))# 发送10,产出平均值10.0print(avg_cor.send(20))# 发送20,产出平均值15.0

send()如何工作

理解 send()方法,关键在于明白它和 yield关键字的关系。

  1. yield的双重角色:yield不仅用于产出值(向生成器外部),其本身也是一个可以接收值的表达式(从生成器外部)。当生成器执行到 yield语句暂停时,它实际上在等待一个值。

  2. send(value)的职责:send(value)方法做两件事:
    传值:将参数 value发送到生成器内部,这个值会成为当前暂停的yield表达式的结果
    恢复执行:恢复生成器的运行,直到下一个 yield或函数结束。
    与只能恢复执行的 next()方法相比,send()的核心优势在于它能传递数据到生成器内部

defsimple_coroutine():print("-> 协程启动")# yield 表达式在这里暂停,并等待接收一个值x=yield"产出值: 1"print(f"-> 协程接收到了:{x}")y=yield"产出值: 2"print(f"-> 协程接收到了:{y}")print("-> 协程结束")# 创建生成器对象coro=simple_coroutine()# 第一步:必须使用 next() 或 send(None) 来"启动"或"预激"生成器,使其运行到第一个 yield 处暂停。# 此时生成器产出 "产出值: 1"first_yield=next(coro)print(f"主程序收到:{first_yield}")# 第二步:使用 send 发送数据# send("数据A") 会恢复生成器,并将 "数据A" 赋值给上一个 yield 表达式(即 x = yield ... 中的 yield)# 生成器继续执行,打印出接收到的数据,运行到下一个 yield 处暂停,并产出 "产出值: 2"second_yield=coro.send("数据A")print(f"主程序收到:{second_yield}")# 第三步:再次发送数据# 此时将 "数据B" 发送给生成器,赋值给 ytry:coro.send("数据B")exceptStopIteration:print("生成器已执行完毕。")

-> 协程启动
主程序收到: 产出值: 1
-> 协程接收到了: 数据A
主程序收到: 产出值: 2
-> 协程接收到了: 数据B
-> 协程结束
生成器已执行完毕。

其他例子

维护状态的累加器:生成器可以成为一个有记忆的累加器

defaccumulator():total=0whileTrue:# 每次 send 来的值都会累加到 total 上,并返回当前总和value=yieldtotalifvalueisnotNone:total+=value acc=accumulator()next(acc)# 预激,此时 total=0print(acc.send(5))# 输出: 5 (0+5)print(acc.send(10))# 输出: 15 (5+10)

实现简单的协程与任务调度:通过 send()可以在多个生成器之间传递数据和控制权,实现协作式的多任务。例如,一个简单的生产者-消费者模型:

defconsumer():whileTrue:item=yield# 等待生产者发送数据print(f'消费:{item}')defproducer(cons):cons.send(None)# 预激消费者协程foriinrange(3):print(f'生产:{i}')cons.send(i)# 将数据发送给消费者c=consumer()producer(c)

为异步编程奠定基础:send()方法是 Python 中现代异步框架(如 asyncio)早期实现的基础。执行器(Runner)可以利用 send()来协调多个异步任务的执行。


总而言之,可以这样理解:Python最新的协程在语法和API层面已经是一个自成一体的独立概念,但其实现的核心思想——利用“暂停与恢复”来高效处理I/O——其技术根源确实来自于生成器。​ 您可以认为生成器是协程的“底层引擎”或“祖先”,而 async/await是建立在它之上的一套更优雅、更强大的“上层建筑”和“操作界面”。

注意事项

yield from

简化生成器嵌套

最基本的功能是扁平化处理可迭代对象。对比一下使用 yield和 yield from的代码:

# 使用 yield 的繁琐方式defchain_with_yield(*iterables):foritiniterables:foriinit:yieldi# 使用 yield from 的简洁方式defchain_with_yield_from(*iterables):foritiniterables:yieldfromit# 直接委托给子迭代器# 效果完全相同list(chain_with_yield('AB',[1,2]))# 输出: ['A', 'B', 1, 2]list(chain_with_yield_from('AB',[1,2]))# 输出: ['A', 'B', 1, 2]

📞 建立双向通信

yield from更强大的功能是打开一个双向通道,让调用方(外部代码)和子生成器能直接通信,包括使用 .send()方法传值和 .throw()方法抛异常。委托生成器在此主要起连接作用 。

defsub_generator():"""子生成器,真正处理业务逻辑"""total=0count=0whileTrue:try:new_num=yieldtotal# 等待接收数据,并返回当前总和exceptValueError:print("子生成器捕获到异常")breakifnew_numisNone:breakcount+=1total+=new_numreturntotal,count# 生成器的返回值defdelegating_generator():"""委托生成器,使用yield from连接调用方和子生成器"""# yield from 表达式的结果是子生成器的返回值result=yieldfromsub_generator()print(f"子生成器返回的结果:{result}")# 调用方delegator=delegating_generator()next(delegator)# 预激,使代码运行到子生成器的yield处暂停print(delegator.send(10))# 输出:10. 值10直接传给子生成器,收到当前总和10print(delegator.send(20))# 输出:30. 值20直接传给子生成器,收到当前总和30delegator.throw(ValueError)# 在子生成器暂停处抛出异常,子生成器捕获并处理# 输出:# 子生成器捕获到异常# 子生成器返回的结果:(30, 2)


yield from的核心价值在于简化生成器嵌套,并创建调用方与子生成器之间的双向通信通道。它自动处理了迭代、值传递和异常处理等繁琐细节,尤其在实现协程或复杂的数据流管道时非常有用。

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

无线充电技术:基于LCC-S拓扑的无线电能传输仿真模型研究,采用Ansys软件搭建矩形线圈simul...

无线充电/无线电能传输LCC-S拓扑仿真模型 WPT 闭环恒压输出控制,输出电流0-30A可调,标准85k频率。 线圈仿真模型的搭建软件为ansys,线圈类型为矩形线圈。 simulink模型或线圈仿真模型LCC-S拓扑在无线充电系统里属于谐振补偿里的狠角色。今天…

作者头像 李华
网站建设 2026/4/13 2:37:31

Java如何通过JNI实现调用C/C++代码,你知道吗?

在软件开发中,Java调用C/C代码是一项提升性能、复用遗留库的关键技术。其核心是通过Java本地接口(JNI)建立桥梁,让运行在JVM上的Java代码能够与本地机器码交互。理解其原理和正确使用方式,能帮助开发者解决纯Java难以处…

作者头像 李华
网站建设 2026/4/13 13:36:13

《深入理解 Ascend C:华为昇腾 AI 芯片的高性能编程语言》

引言:为什么需要 Ascend C?随着人工智能技术的飞速发展,算力需求呈指数级增长。传统通用处理器(如 CPU)在处理大规模神经网络计算时逐渐显现出性能瓶颈,而 GPU 虽然在并行计算方面表现优异,但其…

作者头像 李华
网站建设 2026/4/12 18:22:16

降AIGC率解读:10大工具+通俗说明推荐

降AIGC率解读:10大工具通俗说明推荐 �� 10大降AIGC工具核心对比速览 工具名称 处理速度 效果显著度 专业术语保留 适用场景 aibiye ⚡⚡⚡⚡ ⭐⭐⭐⭐ ✅✅✅ 高重复率论文紧急降重 aicheck ⚡⚡⚡ ⭐⭐⭐⭐ ✅✅✅✅ 法律/医学…

作者头像 李华
网站建设 2026/4/13 5:19:06

开源敏捷协作平台完整教程:提升团队效率的终极指南

你是否正在为项目管理效率低下而苦恼?传统工具无法满足敏捷团队需求,跨部门协作困难重重,任务进度跟踪犹如雾里看花。我们深知这些问题困扰着无数开发团队,今天将为你呈现开源敏捷协作平台的完整解决方案,助你彻底摆脱…

作者头像 李华
网站建设 2026/4/10 0:55:17

Inter字体家族:打造完美屏幕阅读体验的终极指南

Inter字体家族:打造完美屏幕阅读体验的终极指南 【免费下载链接】inter The Inter font family 项目地址: https://gitcode.com/gh_mirrors/in/inter Inter字体是专为数字屏幕设计的开源字体家族,以其卓越的可读性和优雅的几何风格,成…

作者头像 李华