2026/5/22 12:23:10
网站建设
项目流程
深圳cms建站系统,商城类型的网站怎么做,wordpress上百人,建设银行网站的服务管理生成器#xff1a;惰性求值与协程基础
生成器让你能够按需产生值#xff0c;而不是一次性在内存中构建整个序列#xff0c;这对于处理大型或无限的数据流至关重要 。
生成器函数与yield
任何包含yield关键字的函数都是一个生成器函数。调用它时#xff0c;会返回一个生成器…生成器惰性求值与协程基础生成器让你能够按需产生值而不是一次性在内存中构建整个序列这对于处理大型或无限的数据流至关重要 。生成器函数与yield任何包含yield关键字的函数都是一个生成器函数。调用它时会返回一个生成器对象。defcountdown(n):print(Starting countdown from,n)whilen0:yieldn# 每次执行到yield时暂停返回n的值n-1# 创建生成器对象countercountdown(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():total0.0count0whileTrue:new_valueyieldtotal# yield表达式可以接收外部send进来的值ifnew_valueisNone:# 通常用None作为哨兵值来终止协程breaktotalnew_value count1ifcount0:totaltotal/count# 计算平均值avg_coraverager()next(avg_cor)# 预激prime协程使其运行到第一个yield处暂停print(avg_cor.send(10))# 发送10产出平均值10.0print(avg_cor.send(20))# 发送20产出平均值15.0send()如何工作理解 send()方法关键在于明白它和 yield关键字的关系。yield的双重角色yield不仅用于产出值向生成器外部其本身也是一个可以接收值的表达式从生成器外部。当生成器执行到 yield语句暂停时它实际上在等待一个值。send(value)的职责send(value)方法做两件事传值将参数 value发送到生成器内部这个值会成为当前暂停的yield表达式的结果。恢复执行恢复生成器的运行直到下一个 yield或函数结束。与只能恢复执行的 next()方法相比send()的核心优势在于它能传递数据到生成器内部defsimple_coroutine():print(- 协程启动)# yield 表达式在这里暂停并等待接收一个值xyield产出值: 1print(f- 协程接收到了:{x})yyield产出值: 2print(f- 协程接收到了:{y})print(- 协程结束)# 创建生成器对象corosimple_coroutine()# 第一步必须使用 next() 或 send(None) 来启动或预激生成器使其运行到第一个 yield 处暂停。# 此时生成器产出 产出值: 1first_yieldnext(coro)print(f主程序收到:{first_yield})# 第二步使用 send 发送数据# send(数据A) 会恢复生成器并将 数据A 赋值给上一个 yield 表达式即 x yield ... 中的 yield# 生成器继续执行打印出接收到的数据运行到下一个 yield 处暂停并产出 产出值: 2second_yieldcoro.send(数据A)print(f主程序收到:{second_yield})# 第三步再次发送数据# 此时将 数据B 发送给生成器赋值给 ytry:coro.send(数据B)exceptStopIteration:print(生成器已执行完毕。)- 协程启动主程序收到: 产出值: 1- 协程接收到了: 数据A主程序收到: 产出值: 2- 协程接收到了: 数据B- 协程结束生成器已执行完毕。其他例子维护状态的累加器生成器可以成为一个有记忆的累加器defaccumulator():total0whileTrue:# 每次 send 来的值都会累加到 total 上并返回当前总和valueyieldtotalifvalueisnotNone:totalvalue accaccumulator()next(acc)# 预激此时 total0print(acc.send(5))# 输出: 5 (05)print(acc.send(10))# 输出: 15 (510)实现简单的协程与任务调度通过 send()可以在多个生成器之间传递数据和控制权实现协作式的多任务。例如一个简单的生产者-消费者模型defconsumer():whileTrue:itemyield# 等待生产者发送数据print(f消费:{item})defproducer(cons):cons.send(None)# 预激消费者协程foriinrange(3):print(f生产:{i})cons.send(i)# 将数据发送给消费者cconsumer()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():子生成器真正处理业务逻辑total0count0whileTrue:try:new_numyieldtotal# 等待接收数据并返回当前总和exceptValueError:print(子生成器捕获到异常)breakifnew_numisNone:breakcount1totalnew_numreturntotal,count# 生成器的返回值defdelegating_generator():委托生成器使用yield from连接调用方和子生成器# yield from 表达式的结果是子生成器的返回值resultyieldfromsub_generator()print(f子生成器返回的结果{result})# 调用方delegatordelegating_generator()next(delegator)# 预激使代码运行到子生成器的yield处暂停print(delegator.send(10))# 输出10. 值10直接传给子生成器收到当前总和10print(delegator.send(20))# 输出30. 值20直接传给子生成器收到当前总和30delegator.throw(ValueError)# 在子生成器暂停处抛出异常子生成器捕获并处理# 输出# 子生成器捕获到异常# 子生成器返回的结果(30, 2)yield from的核心价值在于简化生成器嵌套并创建调用方与子生成器之间的双向通信通道。它自动处理了迭代、值传递和异常处理等繁琐细节尤其在实现协程或复杂的数据流管道时非常有用。