返回顶部
首页 > 资讯 > 后端开发 > Python >python协程与asyncio库怎么用
  • 727
分享到

python协程与asyncio库怎么用

2023-06-30 14:06:03 727人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

今天小编给大家分享一下python协程与asyncio库怎么用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1.asynci

今天小编给大家分享一下python协程与asyncio库怎么用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

1.asyncio 异步 I/O 库

Python 中的 asyncio 库提供了管理事件、协程、任务和线程的方法,以及编写并发代码的原语,即 async 和 await

该模块的主要内容:

  • 事件循环:event_loop,管理所有的事件,是一个无限循环方法,在循环过程中追踪事件发生的顺序将它们放在队列中,空闲时则调用相应的事件处理者来处理这些事件;

  • 协程:coroutine,子程序的泛化概念,协程可以在执行期间暂停,等待外部的处理(I/O 操作)完成之后,再从暂停的地方继续运行,函数定义式使用 async关键字,这样这个函数就不会立即执行,而是返回一个协程对象;

  • FutureTaskFuture对象表示尚未完成的计算,Task是 Future的子类,包含了任务的各个状态,作用是在运行某个任务的同时可以并发的运行多个任务。

异步函数的定义

异步函数本质上依旧是函数,只是在执行过程中会将执行权交给其它协程,与普通函数定义的区别是在 def关键字前增加 async

# 异步函数import asyncio# 异步函数async def func(x):    print("异步函数")    return x ** 2ret = func(2)print(ret)

运行代码输入如下内容:

sys:1: RuntimeWarning: coroutine 'func' was never awaited<coroutine object func at 0x0000000002C8C248>

函数返回一个协程对象,如果想要函数得到执行,需要将其放到事件循环 event_loop中。

事件循环 event_loop

event_loop是 asyncio模块的核心,它将异步函数注册到事件循环上。 过程实现方式为:由 loop在适当的时候调用协程,这里使用的方式名为 asyncio.get_event_loop(),然后由 run_until_complete(协程对象) 将协程注册到事件循环中,并启动事件循环。

import asyncio# 异步函数async def func(x):    print("异步函数")    return x ** 2# 协程对象,该对象不能直接运行coroutine1 = func(2)# 事件循环对象loop = asyncio.get_event_loop()# 将协程对象加入到事件循环中,并执行ret = loop.run_until_complete(coroutine1)print(ret)

首先在 python 3.7 之前的版本中使用异步函数是安装上述流程:

  • 先通过 asyncio.get_event_loop()获取事件循环loop对象;

  • 然后通过不同的策略调用 loop.run_until_complete()或者loop.run_forever()执行异步函数。

在 python 3.7 之后的版本,直接使用 asyncio.run() 即可,该函数总是会创建一个新的事件循环并在结束时进行关闭。

最新的官方文档 都采用的是run方法。 官方案例

import asyncioasync def main():    print('hello')    await asyncio.sleep(1)    print('world')asyncio.run(main())

接下来在查看一个完整的案例,并且结合await关键字。

import asyncioimport time# 异步函数1async def task1(x):    print("任务1")    await asyncio.sleep(2)    print("恢复任务1")    return x# 异步函数2async def task2(x):    print("任务2")    await asyncio.sleep(1)    print("恢复任务2")    return xasync def main():    start_time = time.perf_counter()    ret_1 = await task1(1)    ret_2 = await task2(2)    print("任务1 返回的值是", ret_1)    print("任务2 返回的值是", ret_2)    print("运行时间", time.perf_counter() - start_time)if __name__ == '__main__':# 创建一个事件循环    loop = asyncio.get_event_loop()    # 将协程对象加入到事件循环中,并执行    loop.run_until_complete(main())

代码输出如下所示:

任务1
恢复任务1
任务2
恢复任务2
任务1 返回的值是 1
任务2 返回的值是 2
运行时间 2.99929154

上述代码创建了 3 个协程,其中 task1和 task2都放在了协程函数 main中,I/O 操作通过 asyncio.sleep(1)进行模拟,整个函数运行时间为 2.9999 秒,接近 3 秒,依旧是串行进行,如果希望修改为并发执行,将代码按照下述进行修改。

import asyncioimport time# 异步函数1async def task1(x):    print("任务1")    await asyncio.sleep(2)    print("恢复任务1")    return x# 异步函数2async def task2(x):    print("任务2")    await asyncio.sleep(1)    print("恢复任务2")    return xasync def main():    start_time = time.perf_counter()    ret_1,ret_2 = await asyncio.gather(task1(1),task2(2))    print("任务1 返回的值是", ret_1)    print("任务2 返回的值是", ret_2)    print("运行时间", time.perf_counter() - start_time)if __name__ == '__main__':    loop = asyncio.get_event_loop()    loop.run_until_complete(main())

上述代码最大的变化是将task1task2放到了asyncio.gather()中运行,此时代码输出时间明显变短。

任务1
任务2
恢复任务2 # 任务2 由于等待时间短,先返回。
恢复任务1
任务1 返回的值是 1
任务2 返回的值是 2
运行时间 2.0005669480000003

asyncio.gather()可以更换为asyncio.wait()修改代码如下所示:

import asyncioimport time# 异步函数1async def task1(x):    print("任务1")    await asyncio.sleep(2)    print("恢复任务1")    return x# 异步函数2async def task2(x):    print("任务2")    await asyncio.sleep(1)    print("恢复任务2")    return xasync def main():    start_time = time.perf_counter()    done, pending = await asyncio.wait([task1(1), task2(2)])    print(done)    print(pending)    print("运行时间", time.perf_counter() - start_time)if __name__ == '__main__':    loop = asyncio.get_event_loop()    loop.run_until_complete(main())

asyncio.wait()返回一个元组,其中包含一个已经完成的任务集合,一个未完成任务的集合。

gather 和 wait 的区别:

  • gather:需要所有任务都执行结束,如果任意一个协程函数崩溃了,都会抛异常,不会返回结果;

  • wait:可以定义函数返回的时机,可以设置为 FIRST_COMPLETED(第一个结束的), FIRST_EXCEPTION(第一个出现异常的), ALL_COMPLETED(全部执行完,默认的)。

done,pending = await asyncio.wait([task1(1),task2(2)],return_when=asyncio.tasks.FIRST_EXCEPTION)

创建 task

由于协程对象不能直接运行,在注册到事件循环时,是run_until_complete方法将其包装成一个 task对象。该对象是对coroutine对象的进一步封装,它比coroutine对象多了运行状态,例如 pendingrunningfinished,可以利用这些状态获取协程对象的执行情况。

下面显示的将coroutine对象封装成task对象,在上述代码基础上进行修改。

import asyncioimport time# 异步函数1async def task1(x):    print("任务1")    await asyncio.sleep(2)    print("恢复任务1")    return x# 异步函数2async def task2(x):    print("任务2")    await asyncio.sleep(1)    print("恢复任务2")    return xasync def main():    start_time = time.perf_counter()    # 封装 task 对象    coroutine1 = task1(1)    task_1 = loop.create_task(coroutine1)    coroutine2 = task2(2)    task_2 = loop.create_task(coroutine2)    ret_1, ret_2 = await asyncio.gather(task_1, task_2)    print("任务1 返回的值是", ret_1)    print("任务2 返回的值是", ret_2)    print("运行时间", time.perf_counter() - start_time)if __name__ == '__main__':    loop = asyncio.get_event_loop()    loop.run_until_complete(main())

由于task对象是future对象的子类对象,所以上述代码也可以按照下述内容修改:

# task_2 = loop.create_task(coroutine2)task_2 = asyncio.ensure_future(coroutine2)

下面将task对象的各个状态进行打印输出。

import asyncioimport time# 异步函数1async def task1(x):    print("任务1")    await asyncio.sleep(2)    print("恢复任务1")    return x# 异步函数2async def task2(x):    print("任务2")    await asyncio.sleep(1)    print("恢复任务2")    return xasync def main():    start_time = time.perf_counter()    # 封装 task 对象    coroutine1 = task1(1)    task_1 = loop.create_task(coroutine1)    coroutine2 = task2(2)    # task_2 = loop.create_task(coroutine2)    task_2 = asyncio.ensure_future(coroutine2)    # 进入 pending 状态    print(task_1)    print(task_2)    # 获取任务的完成状态    print(task_1.done(), task_2.done())    # 执行任务    await task_1    await task_2    # 再次获取完成状态    print(task_1.done(), task_2.done())    # 获取返回结果    print(task_1.result())    print(task_2.result())    print("运行时间", time.perf_counter() - start_time)if __name__ == '__main__':    loop = asyncio.get_event_loop()    loop.run_until_complete(main())

await task_1表示的是执行该协程,执行结束之后,task.done()返回 Truetask.result()获取返回值。

回调返回值

当协程执行完毕,需要获取其返回值,刚才已经演示了一种办法,使用 task.result()方法获取,但是该方法仅当协程运行完毕时,才能获取结果,如果协程没有运行完毕,result()方法会返回 asyncio.InvalidStateError(无效状态错误)。

一般编码都采用第二种方案,通过add_done_callback()方法绑定回调。

import asyncioimport requestsasync def request_html():    url = 'https://www.csdn.net'    res = requests.get(url)    return res.status_codedef callback(task):    print('回调:', task.result())loop = asyncio.get_event_loop()coroutine = request_html()task = loop.create_task(coroutine)# 绑定回调task.add_done_callback(callback)print(task)print("*"*100)loop.run_until_complete(task)print(task)

上述代码当coroutine执行完毕时,会调用callback函数。

如果回调函数需要多个参数,请使用functools模块中的偏函数(partial)方法

循环事件关闭

建议每次编码结束之后,都调用循环事件对象close()方法,彻底清理loop对象。

2.本节爬虫项目

本节课要采集的站点由于全部都是 coser 图片,所以地址在代码中查看即可。

完整代码如下所示:

import threadingimport asyncioimport timeimport requestsimport lxmlfrom bs4 import BeautifulSoupasync def get(url):    return requests.get(url)async def get_html(url):    print("准备抓取:", url)    res = await get(url)    return res.textasync def save_img(img_url):    # thumbMid_5ae3e05fd3945 将小图替换为大图    img_url = img_url.replace('thumb','thumbMid')    img_url = "Http://mycoser.com/" + img_url    print("图片下载中:", img_url)    res = await get(img_url)    if res is not None:        with open(f'./imgs/{time.time()}.jpg', 'wb') as f:            f.write(res.content)            return img_url,"ok"async def main(url_list):    # 创建 5 个任务    tasks = [asyncio.ensure_future(get_html(url_list[_])) for _ in range(len(url_list))]    dones, pending = await asyncio.wait(tasks)    for task in dones:        html = task.result()        soup = BeautifulSoup(html, 'lxml')        divimg_tags = soup.find_all(attrs={'class': 'workimage'})        for div in divimg_tags:            ret = await save_img(div.a.img["data-original"])            print(ret)if __name__ == '__main__':    urls = [f"http://mycoser.com/picture/lists/p/{page}" for page in range(1, 17)]    totle_page = len(urls) // 5 if len(urls) % 5 == 0 else len(urls) // 5 + 1    # 对 urls 列表进行切片,方便采集    for page in range(0, totle_page):        start_page = 0 if page == 0 else page * 5        end_page = (page + 1) * 5        # 循环事件对象        loop = asyncio.get_event_loop()        loop.run_until_complete(main(urls[start_page:end_page]))

代码说明:上述代码中第一个要注意的是await关键字后面只能跟如下内容:

  • 原生的协程对象;

  • 一个包含await方法的对象返回的一个迭代器。

所以上述代码get_html函数中嵌套了一个协程 get。主函数 main里面为了运算方便,直接对 urls 进行了切片,然后通过循环进行运行。

当然上述代码的最后两行,可以直接修改为:

 # 循环事件对象 # loop = asyncio.get_event_loop() # # loop.run_until_complete(main(urls[start_page:end_page])) asyncio.run(main(urls[start_page:end_page]))

轻松获取一堆高清图片:

python协程与asyncio库怎么用

以上就是“python协程与asyncio库怎么用”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网Python频道。

--结束END--

本文标题: python协程与asyncio库怎么用

本文链接: https://lsjlt.com/news/329501.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • python协程与asyncio库怎么用
    今天小编给大家分享一下python协程与asyncio库怎么用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1.asynci...
    99+
    2023-06-30
  • python协程与 asyncio 库详情
    目录1.asyncio 异步 I/O 库异步函数的定义事件循环 event_loop创建 task回调返回值循环事件关闭2.本节爬虫项目前言: python 中协程概念是从 3.4 ...
    99+
    2024-04-02
  • Golang协程与 asyncio
    golang 协程和 python asyncio 都是并发编程工具。协程是轻量级线程,在同一线程并发运行;asyncio 使用事件循环处理 i/o 事件。golang 协程语法简洁,性...
    99+
    2024-04-15
    golang asyncio python
  • 浅谈Python协程asyncio
    一、协程 官方描述; 协程是子例程的更一般形式。 子例程可以在某一点进入并在另一点退出。 协程则可以在许多不同的点上进入、退出和恢复。 它们可通过 async def 语句来实现。...
    99+
    2024-04-02
  • python协程--asyncio模块(
    在高并发的场景下,python提供了一个多线程的模块threading,但似乎这个模块并不近人如意,原因在于cpython本身的全局解析锁(GIL)问题,在一段时间片内实际上的执行是单线程的。同时还存在着资源争夺的问题。python3.4...
    99+
    2023-01-30
    模块 python 协程
  • 怎么在python3协程中使用asyncio
    怎么在python3协程中使用asyncio?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。Python主要用来做什么Python主要应用于:1、Web开发;2、...
    99+
    2023-06-14
  • python中的asyncio异步协程怎么实现
    这篇“python中的asyncio异步协程怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“python中的async...
    99+
    2023-06-30
  • python 中的 asyncio 异步协程
    目录一、定义协程二、运行协程三、协程回调四、运行多个协程五、run_forever六、多协程中关闭run_forever一、定义协程 asyncio 执行的任务,称为协程,但是Asy...
    99+
    2024-04-02
  • Python协程asyncio异步编程笔记分享
    目录1.事件循环2.协程和异步编程2.1 基本使用 2.2 await 2.3 Task对象 1.事件循环 可以理解成为一个死循环,去检查任务列表中的任务,如果可执行就去执行,如果检...
    99+
    2024-04-02
  • Python协程asyncio模块的演变及高级用法
    目录Python协程及asyncio基础知识定义协程函数及执行方法的演变创建协程任务的演变获取协程任务执行结果通过asyncio.gather获取协程任务执行结果asyncio高级使用方法给任务添加回调函数设置任务超...
    99+
    2022-06-02
    Python 协程 python asyncio模块
  • Python协程及asyncio基础知识有哪些
    小编给大家分享一下Python协程及asyncio基础知识有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Python协程及asyncio基础知识协程(cor...
    99+
    2023-06-15
  • Python标准库asyncio有什么作用
    这篇文章主要讲解了“Python标准库asyncio有什么作用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Python标准库asyncio有什么作用”吧!asyncio 是 Python ...
    99+
    2023-06-02
  • python协程与golang协程的区
    进程、线程和协程 进程的定义: 进程,是计算机中已运行程序的实体。程序本身只是指令、数据及其组织形式的描述,进程才是程序的真正运行实例。 线程的定义: 操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。 进程...
    99+
    2023-01-31
    python golang
  • Python中怎么利用Asyncio实现异步编程
    本篇文章为大家展示了Python中怎么利用Asyncio实现异步编程,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。异步是怎么一回事在传统的顺序编程中, 所有发送给解释器的指令会一条条被执行。此类代码...
    99+
    2023-06-17
  • Python 中怎么使用Asyncio实现异步编程
    Python 中怎么使用Asyncio实现异步编程,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。异步是怎么一回事在传统的顺序编程中, 所有发送给解释器的指令会一条条被执行。...
    99+
    2023-06-17
  • Python 协程与 JavaScript 协程的对比
    目录1、前言2、什么是协程?3、混乱的历史3.1Python协程的进化4、JavaScript协程的进化5、Python协程成熟体5.1协程(coroutine)5.2任务(Task...
    99+
    2024-04-02
  • Python Asyncio库之asyncio.task常用函数详解
    目录前记0.基础1.休眠--asyncio.sleep2.屏蔽取消--asyncio.shield3.超时--asyncio.wait_for4.简单的等待--wait5.迭代可等待...
    99+
    2023-03-01
    Python Asyncio asyncio.task Python Asyncio Python asyncio.task
  • Python Asyncio库之asyncio.task常用函数有哪些
    0.基础在《Python Asyncio调度原理》中介绍了Asyncio的两种调度基本单位,Handler和TimeHandler,他们只能被loop.call_xx函数调用,开发者从表面上不知道他们的存在,他们和loop.call_xx属...
    99+
    2023-05-14
    Python asyncio
  • Python:线程、进程与协程(1)——
            最近的业余时间主要放在了学习Python线程、进程和协程里,第一次用python的多线程和多进程是在两个月前,当时只是简单的看了几篇博文然后就跟着用,没有仔细去研究,第一次用的感觉它们其实挺简单的,最近这段时间通过看书, 看...
    99+
    2023-01-31
    线程 进程 Python
  • Python:线程、进程与协程(2)—
        上一篇博文介绍了Python中线程、进程与协程的基本概念,通过这几天的学习总结,下面来讲讲Python的threading模块。首先来看看threading模块有哪些方法和类吧。主要有:Thread :线程类,这是用的最多的一个类,...
    99+
    2023-01-31
    线程 进程 Python
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作