Python 高级编程之生成器与协程进阶(五)
文章目录
- 一、概述
- 二、生成器
- 1)生成器和迭代器的区别
- 2)生成器创建方式
- 1、通过生成器函数创建
- 2、通过生成器表达式创建
- 3)生成器表达式
- 4)yield关键字
- 5)生成器函数
- 6)return 和 yield 异同
- 7)yield的使用方法
- 8)for与next
- 9)send的使用
- 三、协程进阶
- 1)生成器与协程关系
- 2)协程实现原理
- 3)协程实现方式
一、概述
-
生成器是一种在 Python 中的迭代器生成器。生成器是一个函数,它生成一个迭代器。当生成器函数被调用时,它不会立即执行,而是返回一个生成器对象,该对象可以被用于迭代。生成器可以利用 yield 语句在函数内部生成值,并在函数调用者处接收这些值。
-
协程是一种高效的、内存友好的、线程内的并发技术,它可以让您在单个线程内并发地执行多个任务。协程是通过使用 async 关键字实现的,并可以在 Python 中的
asyncio库中使用。与线程不同,协程不需要额外的系统线程,因此它们比线程更高效、更灵活。
在简单的说法,生成器用于生成一系列的值,而协程用于在单个线程中并发执行多个任务。

二、生成器
生成器表达式本质上就是一个迭代器,是定义迭代器的一种方式,是允许自定义逻辑的迭代器。生成器使用generator表示。
-
生成器可以使用 for 循环或 next() 函数来遍历。当生成器对象被创建时,它会保存函数的当前状态,并在每次调用 next() 或 for 循环时从当前状态开始执行,直到遇到 yield 语句为止。
-
当生成器遇到
yield语句时,它会生成当前的值,并保存函数的当前状态,以便下次调用时可以从该状态开始继续执行。当生成器再次被调用时,它会继续执行从上次暂停的地方开始,直到遇到下一个yield或者return语句,或者函数结束为止。
下面是一个生成器函数的示例:
def my_generator():for i in range(3):yield igen = my_generator()
for i in gen:print(i)
输出:
0
1
2
从上面的示例可以看出,生成器的工作原理是通过保存函数的当前状态,以便每次调用时从当前状态开始继续执行,并使用 yield 语句生成值的。
1)生成器和迭代器的区别
生成器和迭代器是 Python 中的两个相关的概念,但是有一些区别:
-
定义:生成器是一种特殊的迭代器,它可以生成一系列的值,而迭代器是一个对象,它实现了 iter 和 next 方法,可以返回一个值的序列。
-
创建:生成器可以通过定义生成器函数,在函数内部使用 yield 语句生成值;迭代器可以通过定义迭代器类,在类中实现 iter 和 next 方法。
-
效率:生成器函数在生成值时只需要暂停函数的执行,因此它具有更高的效率;迭代器类需要维护一个对象状态,因此效率较低。
-
用途:生成器适用于生成大量的数据,因为它可以在生成数据时保存函数的状态,从而避免占用大量内存;迭代器适用于处理少量数据,因为它需要创建一个对象维护状态。
因此,在实际开发中,我们可以根据数据量和处理效率的需求来选择使用生成器或迭代器。
2)生成器创建方式
在 Python 中,可以通过以下两种方式创建生成器:
1、通过生成器函数创建
通过在函数中使用 yield 语句,可以将函数变为生成器函数,每次调用生成器函数时,可以生成一个生成器。
例如:
def generator_example():yield 1yield 2yield 3gen = generator_example()
print(next(gen))
print(next(gen))
print(next(gen))
2、通过生成器表达式创建
生成器表达式是一种简写的生成器创建方式,它基于列表推导式的语法。
例如:
gen = (x for x in range(3))
print(next(gen))
print(next(gen))
print(next(gen))
以上是生成器的两种创建方式,您可以根据实际需求选择使用。
3)生成器表达式
生成器表达式是一种简写的生成器创建方式,它基于列表推导式的语法。
例如:
gen = (x for x in range(3))
print(next(gen))
print(next(gen))
print(next(gen))
在上面的例子中,我们使用生成器表达式创建了一个生成器,该生成器生成从 0 到 2 的整数。然后,我们使用 next() 函数逐个迭代生成器中的值。
4)yield关键字
yield 关键字是 Python 中的一个关键字,用于生成器函数中。它允许一个函数在生成值时暂停其执行,以便在稍后恢复其执行并生成下一个值。这使生成器函数成为一种特殊的函数,可以按需生成一系列值。
5)生成器函数
生成器函数是 Python 中特殊的函数,该函数可生成一个生成器。与普通函数不同,生成器函数可以在每次被调用时生成一个生成器,并在生成器中生成一系列值。
生成器函数通过使用 yield 语句创建生成器。每当函数执行到 yield 语句时,生成器函数的执行就会暂停,并返回 yield 语句后面的值。当再次调用生成器函数时,它将从上次暂停的位置继续执行,直到遇到下一个 yield 语句,或者函数返回。
例如:
def generator_example():yield 1yield 2yield 3gen = generator_example()
print(next(gen))
print(next(gen))
print(next(gen))
在上面的例子中,我们定义了一个生成器函数 generator_example,该函数通过使用 yield 语句生成了三个整数:1、2 和 3。然后,我们通过调用该函数并将其结果分配给生成器 gen 来创建生成器,并使用 next() 函数逐个迭代生成器中的值。
6)return 和 yield 异同
return 和 yield 都是用于在函数中终止执行的关键字,但是它们的作用是不同的。
-
return:当函数调用 return 时,函数立即终止执行,并返回一个值(如果存在)给调用者。该值通常表示函数的最终结果。 -
yield:当生成器函数调用 yield 时,它仅暂停其执行并生成一个值,但不终止函数。在下一次调用该生成器时,它将恢复其执行,直到遇到下一个yield或终止函数。
因此,yield 是生成器函数的一个关键字,可以使生成器生成一系列值,而 return 是一般函数的一个关键字,它返回一个值并终止函数。
7)yield的使用方法
yield 关键字用于生成器函数。在生成器函数中,我们可以使用 yield 关键字生成一系列值,而无需暂停整个函数。
例如,以下是使用 yield 关键字的简单生成器函数的例子:
def simple_generator():yield 1yield 2yield 3yield 4yield 5gen = simple_generator()
print(next(gen)) # Output: 1
print(next(gen)) # Output: 2
print(next(gen)) # Output: 3
print(next(gen)) # Output: 4
print(next(gen)) # Output: 5
我们可以使用 for 循环或 next() 函数迭代生成器中的值,如下所示:
for value in simple_generator():print(value)# Output:
# 1
# 2
# 3
# 4
# 5
【注意】生成器函数只能迭代一次,所以请确保在使用生成器函数时存储其返回值。
8)for与next
在使用生成器时,我们可以使用两种不同的方法来迭代生成器中的值:for 循环和 next() 函数。
- for 循环:通过使用 for 循环,我们可以在生成器中迭代所有值。例如:
def simple_generator():yield 1yield 2yield 3for value in simple_generator():print(value)# Output:
# 1
# 2
# 3
- next() 函数:通过使用 next() 函数,我们可以手动控制生成器的迭代。例如:
def simple_generator():yield 1yield 2yield 3gen = simple_generator()
print(next(gen)) # Output: 1
print(next(gen)) # Output: 2
print(next(gen)) # Output: 3
【注意】在生成器迭代完所有的值后,再使用
next()函数将导致抛出 StopIteration 异常。因此,在使用 next() 函数时,请确保捕获该异常。
9)send的使用
send() 方法是生成器的一种方法,它允许我们在生成器函数内部向生成器发送数据。该方法允许生成器接收外部数据,并使用这些数据生成结果。
在使用 send() 方法时,我们需要在生成器函数中使用 yield 表达式来接收数据,如下所示:
def simple_generator():result = yieldprint("Received data:", result)gen = simple_generator()
next(gen)
gen.send("Hello, World!")
# Output: Received data: Hello, World!
【注意】第一次使用 send() 方法之前,我们需要调用 next() 函数,以启动生成器函数。此外,使用 send() 方法将导致抛出 StopIteration 异常,因此请确保在使用该方法时进行异常处理。
三、协程进阶
协程(Coroutine)是一种编程技巧,用于在多任务环境中实现轻量级的任务切换。与线程不同,协程不需要创建新的系统级线程,并且消耗的资源也比线程更少。因此,协程可以比线程更高效地完成任务。在我上篇的文章已经讲解了:IO模型和协程介绍
1)生成器与协程关系
生成器和协程是相关但有所不同的概念。生成器是一种特殊的迭代器,可以生成一系列的值,每次迭代时只返回一个值。生成器可以使用 yield 关键字来暂停执行,并在下一次调用时继续执行。
-
协程是一种并发编程技术,可以在单一线程中实现多个任务的并行执行。与线程不同,协程不需要创建新的系统级线程,并且消耗的资源也比线程更少。协程可以使用
yield关键字来暂停执行,并在下一次调用时继续执行。 -
因此,生成器可以用于实现协程,但它们不是协程的必需条件。在 Python 中,可以使用
asyncio库来实现协程,该库不需要使用生成器。不过,在实现协程时,生成器确实可以作为一种有效的工具,帮助开发者实现协程的暂停和恢复。
2)协程实现原理
协程的实现主要是通过一种叫做 “协程调度器” 的技术实现的,这种技术能够在不创建新的线程的情况下,在单一线程中按需切换协程的执行。协程调度器会管理当前正在运行的协程以及等待执行的协程,并在每个协程执行完后切换到下一个协程。
3)协程实现方式
在 Python 中,可以使用 asyncio 库来实现协程。协程函数可以使用 async 关键字标记,表示该函数是一个协程函数。在协程函数中,可以使用 await 关键字等待其他协程完成,从而实现协程的切换。
在 Python 中,可以使用 asyncio 库来实现协程。下面是一个简单的例子:
import asyncioasync def task1():print("Task 1 is running")await asyncio.sleep(1)print("Task 1 is complete")async def task2():print("Task 2 is running")await asyncio.sleep(1)print("Task 2 is complete")async def main():task1_coro = task1()task2_coro = task2()await asyncio.gather(task1_coro, task2_coro)if __name__ == "__main__":asyncio.run(main())
上面的代码中,task1 和 task2 是两个协程函数,它们可以在单独的任务中并行执行。main 函数是协程的入口,在这个函数中,我们创建了两个协程的对象 task1_coro 和 task2_coro,并通过 asyncio.gather 函数将它们并行执行。最后,我们通过 asyncio.run 函数启动了整个协程。
运行上面的代码,可以得到以下输出:
Task 1 is running
Task 2 is running
Task 1 is complete
Task 2 is complete
可以看到,协程中的任务是并行执行的,因此我们可以充分利用 CPU 的资源,提高程序的执行效率。
Python 高级编程之生成器与协程进阶讲解就先到这里了,有任何疑问欢迎给我留言,后续会持续更新相关技术文章,请小伙伴耐心等待,也可以关注我的公众号【大数据与云原生技术分享】进行深入技术交流~
相关文章:
Python 高级编程之生成器与协程进阶(五)
文章目录一、概述二、生成器1)生成器和迭代器的区别2)生成器创建方式1、通过生成器函数创建2、通过生成器表达式创建3)生成器表达式4)yield关键字5)生成器函数6)return 和 yield 异同7)yield的使…...
Django框架之视图和URL
视图和URL 站点管理页面做好了, 接下来就要做公共访问的页面了.对于Django的设计框架MVT. 用户在URL中请求的是视图.视图接收请求后进行处理.并将处理的结果返回给请求者.使用视图时需要进行两步操作 1.定义视图2.配置URLconf 1. 定义视图 视图就是一个Python函数,…...
Python 的Tkinter包系列之七:好例子补充2
Python 的Tkinter包系列之七:好例子补充2 英汉字典(使用文本文件记录英语单词和解释)、简单的通信录(使用SQLite数据库记录人员信息) 一、tkinter编写英汉字典 先看效果图: 词典文件是一个文本文件&…...
每日一练-等差数列
等差数列🍀题目描述🌿解题思路🌸Python源码📧Summary📆Date: 2023年2月10日 🎬Author: 小 y 同 学 📃Classify: 蓝桥杯每日一练 🔖Language: Python 🍀题目描述 题意 …...
使用动态参数构建CUDA图
文章目录使用动态参数构建CUDA图使用显式 API 调用构建 CUDA 图使用流捕获构建 CUDA 图组合方法执行结果总结使用动态参数构建CUDA图 自从在 CUDA 10 以来,CUDA Graphs 已被用于各种应用程序。 上图将一组 CUDA 内核和其他 CUDA 操作组合在一起,并使用指…...
在Fortran中调用Python教程
前言Python是机器学习领域不断增长的通用语言。拥有一些非常棒的工具包,比如scikit-learn,tensorflow和pytorch。气候模式通常是使用Fortran实现的。那么我们应该将基于Python的机器学习迁移到Fortran模型中吗?数据科学领域可能会利用HTTP AP…...
04-PS人像磨皮方法
1.高斯模糊磨皮 这种方法的原理就是建立一个将原图高斯模糊后图层, 然后用蒙版加画笔或者历史画笔工具将需要磨皮的地方涂抹出来, 通过图层透明度, 画笔流量等参数来控制磨皮程度 1.新建图层(命名为了高斯模糊磨皮), 混合模式设置为正常, 然后选择高斯模糊, 模糊数值设置到看…...
nginx反向代理+负载均衡上传webshell重难点+apache漏洞
nginx反向代理 nginx 负载均衡 负载均衡的策略 1、轮询:nginx默认就是轮询其权重都默认为1,服务器处理请求的顺序:ABABABABAB… upstream mysvr { server 192.168.137.131; server 192.168.137.136; }2、weight:跟据配置…...
transition组件的使用
<template><button click"flag !flag">切换</button><transition name"fade"><div v-if"flag" class"box"></div></transition> </template><script setup lang"ts"&g…...
多行文本在块元素中垂直居中
单行文本垂直居中对齐 在块元素中,让单行文本居中,可以使用line-height等于块元素的高,即可让该单行文本垂直居中对齐。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><me…...
在 WebAssembly 中使用 C/C++ 和 libbpf 编写 eBPF 程序
作者:于桐,郑昱笙 eBPF(extended Berkeley Packet Filter)是一种高性能的内核虚拟机,可以运行在内核空间中,用来收集系统和网络信息。随着计算机技术的不断发展,eBPF 的功能日益强大,…...
leveldb源码解析六——compact
compact分为manual_compaction、minor_compaction、major_compaction,统一由MaybeScheduleCompaction触发: void DBImpl::MaybeScheduleCompaction() {mutex_.AssertHeld();if (background_compaction_scheduled_) {// Already scheduled} else if (shu…...
数据结构(二):单向链表、双向链表
数据结构(二)一、什么是链表1.数组的缺点2.链表的优点3.链表的缺点4.链表和数组的区别二、封装单向链表1. append方法:向尾部插入节点2. toString方法:链表元素转字符串3. insert方法:在任意位置插入数据4.get获取某个…...
COCO物体检测评测方法简介
本文从ap计算到map计算,最后到coco[0.5:0.95:0.05] map的计算,一步一步拆解物体检测指标map的计算方式。 一、ap计算方法 一个数据集有多个类别,对于该数据库有5个gt,算法检测出来10个bbox,对于人这个类别来说检测有…...
记一次上环境获取资源失败的案例
代码结构以及资源位置 测试代码 RestController RequestMapping("/json") public class JsonController {GetMapping("/user/1")public String queryUserInfo() throws Exception {// 如果使用全路径, 必须使用/开头String path JsonController.class.ge…...
实战超详细MySQL8离线安装
在RedHat中,RPM Bundle 方式安装MySQL8。建议一定要用 RPM Bndle 版本安装,包全。官网下载:https://dev.mysql.com/downloads/mysql/1.卸载mariadb,会与MySQL安装冲突。rpm -qa | grep mariadb 查看有无mariadb如果有࿰…...
依赖倒置原则|SOLID as a rock
文章目录 意图动机:违反依赖倒置原则解决方案:C++中依赖倒置原则的例子依赖倒置原则的优点1、可复用性2、可维护性在C++中用好DIP的标准总结本文是关于 SOLID as Rock 设计原则系列的五部分中的 最后一部分。 SOLID 设计原则侧重于开发 易于维护、可重用和可扩展的软件。 在…...
Webpack的知识要点
在前端开发中,一般情况下都使用 npm 和 webpack。 npm是一个非常流行的包管理工具,帮助开发者管理项目中使用的依赖库和工具。它可以方便地为项目安装第三方库,并在项目开发过程中进行版本控制。 webpack是一个模块打包工具ÿ…...
handler解析(2) -Handler源码解析
目录 基础了解: 相关概念解释 整体流程图: 源码解析 Looper 总结: sendMessage 总结: ThreadLocal 基础了解: Handler是一套 Android 消息传递机制,主要用于线程间通信。实际上handler其实就是主线程在起了一…...
【算法】kmp
KMP算法 名称由来 是由发明这个算法的三个科学家的名称首字母组成 作用 用于字符串的匹配问题 举例说明 字符串 aabaabaaf 模式串 aabaaf 传统匹配方法 第一步 aabaabaaf aabaaf 此时,b和f不一致,则把模式串从头和文本串的第二个字符开始比 第…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
