Python异步编程模型实战教程

在本教程中,我们介绍Python协程以及如何使用Python async和await关键字来创建和暂停协程。这种异步编程模型在处理大量 I/O 操作(如网络请求、文件读取等)时特别有用,可以避免程序因为等待这些操作而被阻塞。

介绍Python 协程

在编程世界中,执行每个任务需要的时间不同。有的任务可能很快,而有些任务可能需要等待外部资源,例如获取来自服务器的数据或用户输入。作为开发人员,我们经常遇到这样的情况: 我们需要同时执行多个任务,而不是在开始新的任务之前等待前面每个任务都完成。这就是异步编程发挥作用的地方,Python中的asyncio模块是实现这一目标的强大工具。

在传统的同步编程中,任务是顺序地执行,程序等待每个任务完成后再进入下一个任务。在异步编程中,我们可以使用async关键字将某些函数标记为异步,从而允许它们与其他任务并发运行。我们把async标记的函数称为协程,协程可以在运行过程中暂停执行,以实现并发运行多个协程。

要创建和暂停协程,可以使用Python async和await关键字:

  • async关键字创建一个协程。
  • await关键字暂停协程。

定义协程

下面定义了简单的square函数,简单返回整数的平方数:

def square(number: int) -> int:return number*number

可以将一个整数作为参数传给square()函数,来获得它的平方数:

def square(number: int) -> int:return number*numberresult = square(10)
print(result) // 100

当给函数添加async关键字时,该函数将成为协程:

async def square(number: int) -> int:return number*number

调用协程返回协程对象,然后打印结果:

async def square(number: int) -> int:return number*numberresult = square(10)
print(result)

输出如下:

<coroutine object square at 0x00000185C31E7D80>
sys:1: RuntimeWarning: coroutine 'square' was never awaited

在本例中,我们调用square()协程,将返回值赋给result变量,并将其打印出来。当你调用协程时,Python不会立即执行协程内的代码;相反,它仅返回协程对象。

输出中的第二行还显示了一条错误消息,表明从未等待协程。在下面的await部分中可以看到更多:

sys:1: RuntimeWarning: coroutine 'square' was never awaited

Go 语言的协程是轻量级的线程,称为 goroutine,Go 语言的并发模型是基于 CSP(Communicating Sequential Processes,通信顺序进程)模型。Go 语言运行时会自动调度 goroutine 在多个操作系统线程上执行。而Python 的协程基于事件循环模型,通过异步 I/O 和回调来实现并发。要运行协程,需要在事件循环中执行它。在Python 3.7之前,你必须手动创建事件循环来执行协程并关闭事件循环。

然而,从3.7版开始,asyncio库添加了一些简化事件循环管理的函数。例如,您可以使用asyncio.run()函数自动创建事件循环,运行协程并关闭它。下面的代码使用asyncio.run()函数来执行square()协程并获得结果:

import asyncioasync def square(number: int) -> int:return number*numberresult = asyncio.run(square(10))
print(result)  // 100

特别要注意是,asyncio.run()被设计为asyncio程序的主要入口点。此外,asyncio.run()函数只执行一个协程,该协程可以调用程序中的其他协程。

暂停协程

await关键字暂停协程的执行。await关键字后面是对协程的调用,如下所示:

Result = await my_coroutine()

await关键字导致my_coroutine()执行并等待代码完成、返回结果。要注意,await关键字仅在协程中有效。换句话说,必须在协程中使用await关键字。

这就是为什么在上面的示例中看到的错误消息:需要在协程上使用await关键字。下面的例子展示了如何使用await关键字来暂停协程:

import asyncioasync def square(number: int) -> int:return number*numberasync def main() -> None:x = await square(10)print(f'x={x}')y = await square(5)print(f'y={y}')print(f'total={x+y}')if __name__ == '__main__':asyncio.run(main())

输出结果:

x=100
y=25
total=125

它是如何工作的。我们将重点关注main()函数 :

首先,使用await关键字调用square()协程。await关键字将暂停main()协程的执行,等待square()协程完成,并返回结果:

x = await square(10)
print(f'x={x}')

其次,使用await关键字第二次调用square()协程:

y = await square(5) 
print(f' y={y}')

第三,显示总数:

print(f 'total = {x + y}')

下面的语句使用run()函数来执行main()协程并管理事件循环:

asyncio.run(main())

到目前为止,我们的程序像同步程序一样执行。它没有揭示并发模型的强大功能,但通过简单示例让你更清晰掌握异步任务创建、执行。

完整示例

以下是一个更详细的 Python 中asyncawait关键字的示例及解释:

import asyncioasync def slow_operation(name, duration):print(f"Starting {name}...")await asyncio.sleep(duration)print(f"{name} completed.")return f"{name} result"async def main():task1 = slow_operation("Task 1", 2)task2 = slow_operation("Task 2", 3)# 同时执行两个异步任务results = await asyncio.gather(task1, task2)print(results)asyncio.run(main())
  • async def定义了一个异步函数。在这个例子中,slow_operationmain都是异步函数。
  • slow_operation模拟一个耗时的操作,这里使用asyncio.sleep来暂停执行指定的时间。当遇到await asyncio.sleep(duration)时,这个异步函数会暂停执行,将控制权交还给事件循环,让事件循环可以去执行其他的任务。
  • main函数中,首先创建了两个异步任务task1task2,分别调用slow_operation。然后使用asyncio.gather来同时执行这两个任务,并等待它们全部完成。asyncio.gather会返回一个包含所有任务结果的列表。
  • 最后,使用asyncio.run(main())来运行主异步函数,它会创建一个新的事件循环并在其中运行main函数,直到main函数完成后关闭事件循环。

场景实战

前面示例仅为模拟任务,下面是使用asyncawait的实际场景示例,。这个示例展示如何在等待多个网页下载的同时,不会阻塞程序的执行,可以提高程序的效率。

import asyncio
import aiohttpasync def download_page(session, url):"""异步函数,用于下载指定 URL 的网页内容。参数:- session:aiohttp 的客户端会话对象。- url:要下载的网页 URL。"""async with session.get(url) as response:if response.status == 200:return await response.text()else:return Noneasync def main():"""主异步函数,创建任务列表并等待所有任务完成。"""urls = ["https://www.example.com","https://www.example.org","https://www.example.net"]async with aiohttp.ClientSession() as session:tasks = [download_page(session, url) for url in urls]pages = await asyncio.gather(*tasks)for url, page in zip(urls, pages):if page:print(f"Downloaded {url} successfully.")else:print(f"Failed to download {url}.")if __name__ == "__main__":asyncio.run(main())

解释:

  • download_page函数接受一个aiohttp的会话对象和一个 URL 作为参数。使用async with语句创建一个异步的 HTTP GET 请求。如果响应状态码是 200,表示请求成功,就使用await response.text()获取网页内容并返回。如果状态码不是 200,则返回None
  • main函数中定义了要下载的网页 URL 列表。使用aiohttp.ClientSession创建一个客户端会话对象,这个对象可以在多个请求之间复用连接以提高性能。
  • 通过列表推导式创建一个任务列表,每个任务都是调用download_page函数,传入不同的 URL 和会话对象。
  • 使用asyncio.gather同时执行所有任务,并等待它们全部完成。asyncio.gather会返回一个包含所有任务结果的列表,按照任务的调用顺序排列。
  • 最后,遍历 URL 和对应的网页内容,如果内容不为None,表示下载成功,打印成功信息;否则,打印下载失败信息。

总结

  • 协程是一种常规函数,它能够暂停当前正在执行的任务,去执行其他长时间运行的操作、等待结果,并从暂停点恢复。
  • 使用async关键字定义协程,使用await关键字暂停协程。使用asyncio.run()函数在事件循环上自动执行协程并管理事件循环。

总之,使用asyncawait关键字可以编写异步代码,使得程序在等待某些耗时操作时可以继续执行其他任务,提高程序的效率和响应性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/55433.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【基础算法总结】链表篇

目录 一&#xff0c; 链表常用技巧和操作总结二&#xff0c;算法原理和代码实现2.两数相加24.两两交换链表中的节点143.重排链表23.合并k个升序链表25.k个一组翻转链表 三&#xff0c;算法总结 一&#xff0c; 链表常用技巧和操作总结 有关链表的算法题也是一类常见并且经典的题…

Linux命令大全及小例子

撰写一份关于Linux命令大全的详尽报道和分析是一项重要的任务&#xff0c;旨在让读者全面了解Linux命令的用途和应用场景。Linux系统因其强大的命令行工具而闻名&#xff0c;无论是系统管理、文件操作还是网络配置&#xff0c;Linux命令行都提供了灵活且强大的解决方案。以下是…

python字典的pop方法

在Python中&#xff0c;字典&#xff08;dict&#xff09;的 pop 方法用于删除字典中的特定键&#xff08;key&#xff09;并返回该键对应的值。如果键不存在于字典中&#xff0c;可以指定一个默认值返回&#xff0c;否则会抛出 KeyError。 以下是 pop 方法的一些用法示例&…

保险丝基础知识

一、简介 保险丝&#xff08;fuse&#xff09;也被称为电流保险丝&#xff0c;它能够在电流异常升高到一定的高度和热度时&#xff0c;自动熔断切断电流&#xff0c;从而保护电路安全运行。 IEC127标准将它定义为“熔断体&#xff08;fuse-link)”。熔断体是由电阻率比较大而熔…

初识Linux · 文件(1)

目录 前言&#xff1a; 回顾语言层面的文件 理解文件的预备知识 文件和磁盘 使用和认识系统调用函数 前言&#xff1a; 本文以及下篇文章&#xff0c;揭露的都是Linux中文件的奥秘&#xff0c;对于文件来说&#xff0c;初学Linux第一节课接触的就是文件&#xff0c;对于C…

蓝禾,汤臣倍健,三七互娱,得物,顺丰,快手,游卡,oppo,康冠科技,途游游戏,埃科光电25秋招内推

蓝禾&#xff0c;汤臣倍健&#xff0c;三七互娱&#xff0c;得物&#xff0c;顺丰&#xff0c;快手&#xff0c;游卡&#xff0c;oppo&#xff0c;康冠科技&#xff0c;途游游戏&#xff0c;埃科光电25秋招内推 ①蓝禾 【岗位】国内/国际电商运营&#xff0c;设计&#xff0c;研…

Hive数仓操作(十六)

DML&#xff08;数据操作语言&#xff09;指的是用于操作数据的 SQL 语言部分&#xff0c;主要包括对数据的插入、更新、删除等操作。Hive 的 DML语句主要包括 INSERT、UPDATE 和 DELETE 。以下是一些重要的 Hive DML 语句及其解析。 Hive的DML语句 一、 插入操作INSERT 一般…

在树莓派上部署开源监控系统 ZoneMinder

原文&#xff1a;https://blog.iyatt.com/?p17425 前言 自己搭建&#xff0c;可以用手里已有的设备&#xff0c;不需要额外买。这套系统的源码是公开的&#xff0c;录像数据也掌握在自己手里&#xff0c;不经过不可控的三方。 支持设置访问账号 可以保存录像&#xff0c;启…

《深入理解java虚拟机》——java内存区域与内存溢出异常

我是很喜欢用java语言编写代码的。从开始学习到现在其实也是在一步步体会java语言的各方面&#xff0c;开始看深入理解java虚拟机这本书觉得java虚拟机的内部感觉就像是一个操作系统&#xff0c;也可以说是个计算机。想要深入的理解我觉得需要先从整体去看。为什么需要java虚拟…

ProtoBuf快速上手

文章目录 创建 .proto文件编译 .proto文件编译后生成的文件序列化与反序列化的使用 此篇文章实现内容&#xff1a; 对一个通讯录的联系人信息&#xff0c;使用PB进行序列化&#xff0c;并将结果输出对序列化的内容使用PB进行反序列化&#xff0c;解析联系人信息并输出联系人信…

redis-数据类型

十大数据类型 学习 redis 操作手册 英文 Commands 中文 Redis命令中心&#xff08;Redis commands&#xff09; – Redis中国用户组&#xff08;CRUG&#xff09; 学习方法 举出一个数据结构的应用场景&#xff08;理解数据结构特点&#xff09;&#xff0c;并操作&…

Django学习笔记一:MVT的示例

Django的MVT&#xff08;Model-View-Template&#xff09;架构是一种将应用程序的不同部分分离的方法&#xff0c;旨在提高代码的可维护性和可扩展性。MVT将应用分解为三个主要部分&#xff1a;Model&#xff08;模型&#xff09;、View&#xff08;视图&#xff09;和Template…

【Java的SPI机制】Java SPI机制:实现灵活的服务扩展

在Java开发中&#xff0c;SPI&#xff08;Service Provider Interface&#xff0c;服务提供者接口&#xff09;机制是一种重要的设计模式&#xff0c;它允许在运行时动态地插入或更换组件实现&#xff0c;从而实现框架或库的扩展点。本文将深入浅出地介绍Java SPI机制&#xff…

JAVA开源项目 旅游管理系统 计算机毕业设计

本文项目编号 T 063 &#xff0c;文末自助获取源码 \color{red}{T063&#xff0c;文末自助获取源码} T063&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析5.4 用例设计 六、核…

TypeScript 封装 Axios 1.7.7

随着Axios版本的不同&#xff0c;类型也在改变&#xff0c;以后怎么写类型&#xff1f; yarn add axios1. 封装Axios 将Axios封装成一个类&#xff0c;同时重新封装request方法 重新封装request有几个好处&#xff1a; 所有的请求将从我们定义的requet请求中发送&#xff…

Vue3实现动态菜单功能

文章目录 0.效果演示1.搭建Vue3项目1.1 vite 脚手架创建 Vue3 项目1.2 设置文件别名1.3 安装配置 element-plus1.4 安装配置路由2.登录页面3.后台管理页面3.1 搭建后台框架3.2 左侧菜单栏3.3 header 用户信息3.4 主要内容3.5 footer4.配置静态路由5.记录激活菜单5.1 el-menu 绑…

卸载apt-get 安装的PostgreSQL版本

文章目录 卸载apt-get 安装的PostgreSQL版本查找已安装的PostgreSQL包卸载PostgreSQL&#xff1a;检查并删除残留文件验证卸载 卸载apt-get 安装的PostgreSQL版本 卸载通过apt-get安装的PostgreSQL 就版本&#xff0c;可以按照以下步骤进行。 查找已安装的PostgreSQL包 在卸…

信号处理快速傅里叶变换(FFT)的学习

FFT是离散傅立叶变换的快速算法&#xff0c;可以将一个信号变换到频域。有些信号在时域上是很难看出什么特征的&#xff0c;但是如果变换到频域之后&#xff0c;就很容易看出特征了。这就是很多信号分析采用FFT变换的原因。另外&#xff0c;FFT可以将一个信号的频谱提取出来&am…

StarRocks 中如何做到查询超时(QueryTimeout)

背景 本文基于 StarRocks 3.1.7 主要是分析以下两种超时设置的方式: SESSION 级别 SET query_timeout 10;SELECT sleep(20);SQL 级别 select /* SET_VAR(query_timeout10) */ sleep(20); 通过本文的分析大致可以了解到在Starrocks的FE端是如何进行Command的交互以及数据流走…

Java Web 之 Cookie 详解

在 JavaWeb 开发中&#xff0c;Cookie 就像网站给浏览器贴的小纸条&#xff0c;用于记录一些用户信息或状态&#xff0c;方便下次访问时识别用户身份或进行个性化服务。 也可以这么理解&#xff1a; 场景一&#xff1a;想象一下&#xff0c;你去一家咖啡店&#xff0c;店员认…