Python学习【103】:python中生成器yield的使用与普通函数的区别,进而介绍异步函数
一、学前花絮
我们在学习yield的时候,感觉它就是函数的“暂停键”。它让函数执行到一半停下来,保存现场(变量状态),等下次被唤醒(next())时,再从断点处继续。
这种东西有什么用呢?一下子获取结果不好吗?其实这个问题,对于很多学习python的朋友来说都会问这个问题。但我们想象一下,如果有一个结果集很大很大,千万以上级别。那么我们可能不一定一下子需要这么多,就可以用到yield,随用随产生。如果把数据比作水流,那么yield就是在水管上加上一个开关,不用的时候暂停,用的时候就打开。
而随着学习的深入,我们又接触到异步函数,也似乎有“暂停”的感觉。一查才知道,原来yield与异步函数之间有着密切的联系。
二、Python 中yield生成器与Async/Await异步函数的实现并示例
在 Python 的发展史上,有一条非常清晰的进化线:yield -> 生成器 -> 协程 -> async/await。简单说:async/await 就是官方帮你写好了“驱动器”的生成器。
2.1 yield 与普通函数的区别
普通函数像一条单行道,进去就得一口气跑到底(遇到 return 就死透了);而带有 yield 的生成器函数,像是一个可以存档读档的游戏。
核心区别对比表
代码演示:存档与读档
输出如下:
关键点:
生成器函数返回的 gen 是一个对象,它实现了迭代器协议。你可以把它想象成一个工厂流水线,yield 就是“生产一个就发走一个”,而不是把仓库堆满再发货。
2.2 从生成器到异步 (Async) 的进化
现在我们继续这个问题:这个yield“暂停”怎么就变成“异步”了?
思路转变:暂停 = 等待
想象一下,当程序在 yield 处暂停时,CPU 是空闲的。这时候,如果我们不是在等 next() 手动唤醒,而是在等一个网络请求回来,那岂不是就能去干别的事了?
这就是异步的起源。Python 曾经(2.5 - 3.4 版本)大量使用生成器 + yield 来写异步代码。
示例:用生成器模拟异步请求
下面需要设计一个调度器:
认真分析上面的程序后我们发现:用原生生成器写异步逻辑非常痛苦,你需要手动写调度器,手动 send 结果。这就是为什么后来 Python 引入了专门的语法。
2.3 异步函数 (Async/Await) —— 终极形态
Python 3.5 引入了 async 和 await。你可以把 await 理解为 “专门用来等待异步任务的 yield”,而 asyncio 库就是那个官方写好的调度器。
现代写法:Async/Await
总结:三者的关系
- 普通函数 (def + return):一条道走到黑,不能暂停。
- 生成器 (def + yield):通用的暂停器。它创造了“可以暂停的函数”这一概念,这是异步编程的理论基础。
- 异步函数 (async def + await):专用的异步暂停器。
lawait 本质上就是 yield,但它只能 await 一个 awaitable 对象(比如另一个协程)。
lasyncio 库利用生成器的原理,实现了事件循环(调度器),帮我们自动完成了 send 和状态管理。
一句话概括:yield 是让函数学会“暂停”的基础工具;而 async/await 是 Python 官方基于这个工具,专门为了处理 I/O 等待(网络、文件)而打造的自动化流水线。
三、小结
通过学习python的yield生成器与普通函数对比,我们发现这个“暂停键”很不一般。进而了解到异步async/await本质上确实与yield相关,并且在功能上更加强大。我们在学习python中要多思考,很多看似相关的东西,本质上很可能就是一脉相承。
让我们保持学习热情,多做练习。我们下期再见!