Python协程方式的实现及意义笔记分享

 更新时间:2021年09月14日 08:55:54   作者:季布,  
协程也被称为微线程,是一种用户态的上下文切换技术,简而言之,就是通过一个线程实现代码互相切换执行,本文主要给大家介绍实现协程的几种方法

协程

协程不是计算机提供的,是程序员认为创造
协程也被称为微线程,是一种用户态的上下文切换技术,简而言之,就是通过一个线程实现代码互相切换执行
实现协程的几种方法:

  • 1)greenlet,早期模块
  • 2)yield关键字
  • 3)asyncio装饰器 (python3.4以后引入的)
  • 4)async,await关键字 (python3.5) 推荐

1.greenlet实现协程

greentlet是一个第三方模块,需要提前安装 pip3 install greenlet才能使用。

from greenlet import greenlet
def func1():
    print(1)        # 第1步:输出 1
    gr2.switch()    # 第3步:切换到 func2 函数
    print(2)        # 第6步:输出 2
    gr2.switch()    # 第7步:切换到 func2 函数,从上一次执行的位置继续向后执行
def func2():
    print(3)        # 第4步:输出 3
    gr1.switch()    # 第5步:切换到 func1 函数,从上一次执行的位置继续向后执行
    print(4)        # 第8步:输出 4
gr1 = greenlet(func1)
gr2 = greenlet(func2)
gr1.switch() # 第1步:去执行 func1 函数

输出的结果:
1
3
2
4

注意:switch中也可以传递参数用于在切换执行时相互传递值。

2.yield

基于Python的生成器的yield和yield form关键字实现协程代码。

def func1():
    yield 1   #第一步执行这里会生成1
    yield from func2() #这里会跳到func2,然后执行里面的代码,执行完func2函数后会继续以跳转之前的状态继续执行以下代码
    yield 2
def func2():
    yield 3
    yield 4    
# 这里是一个生成器对象
f1 = func1()
# 遍历执行生成器
for item in f1:
    print(item)

执行结果
1
3
4
2

注:用这种方法比较牵强,真正开发环境中基本不会用这种方法实现协程(yield form关键字是在Python3.3中引入的。)

3.asyncio

在Python3.4之前官方未提供协程的类库,一般大家都是使用greenlet等其他来实现。在Python3.4发布后官方正式支持协程,即:asyncio模块。

import asyncio
#这就是一个用协程实现的函数
@asyncio.coroutine
def func1():
    print(1)
    yield from asyncio.sleep(2)  # 遇到IO耗时操作,自动化切换到tasks中的其他任务
    print(2)
loop = asyncio.get_event_loop()
#执行
loop.run_until_complete(func1())

同时执行多个协程

import asyncio
@asyncio.coroutine
def func1():
    print(1)
    yield from asyncio.sleep(2)  # 遇到IO耗时操作,自动化切换到tasks中的其他任务
    print(2)
@asyncio.coroutine
def func2():
    print(3)
    yield from asyncio.sleep(2) # 遇到IO耗时操作,自动化切换到tasks中的其他任务
    print(4)
tasks = [
    asyncio.ensure_future( func1() ),
    asyncio.ensure_future( func2() )
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

结果
1
3
2
4

注意:基于asyncio模块实现的协程比之前的要更厉害,因为他的内部还集成了遇到IO耗时操作自动切花的功能。

4.async & awit

async & awit 关键字在Python3.5版本中正式引入,基于他编写的协程代码其实就是 上一示例 的加强版,让代码可以更加简便。

Python3.8之后 @asyncio.coroutine 装饰器就会被移除,推荐使用async & awit 关键字实现协程代码。

import asyncio
async def func1():
    print(1)
    await asyncio.sleep(2)
    print(2)
async def func2():
    print(3)
    await asyncio.sleep(2)
    print(4)
tasks = [
    asyncio.ensure_future(func1()),
    asyncio.ensure_future(func2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

常用的是:greenlet,async&awit

协程的意义

在一个线程中遇到IO耗时,线程不会等待,利用空余的时间去执行其他的方法

爬虫案例

例如:用代码实现下载 url_list 中的图片。

方式一:同步编程实现

import requests
def download_image(url):
    print("开始下载:",url)
    # 发送网络请求,下载图片
    response = requests.get(url)
    print("下载完成")
    # 图片保存到本地文件
    file_name = url.rsplit('_')[-1]
    with open(file_name, mode='wb') as file_object:
        file_object.write(response.content)
if __name__ == '__main__':
    url_list = [
        'https://www3.autoimg.cn/newsdfs/g26/M02/35/A9/120x90_0_autohomecar__ChsEe12AXQ6AOOH_AAFocMs8nzU621.jpg',
        'https://www2.autoimg.cn/newsdfs/g30/M01/3C/E2/120x90_0_autohomecar__ChcCSV2BBICAUntfAADjJFd6800429.jpg',
        'https://www3.autoimg.cn/newsdfs/g26/M0B/3C/65/120x90_0_autohomecar__ChcCP12BFCmAIO83AAGq7vK0sGY193.jpg'
    ]
    for item in url_list:
        download_image(item)

结果
开始下载: https://www3.autoimg.cn/newsdfs/g26/M02/35/A9/120x90_0_autohomecar__ChsEe12AXQ6AOOH_AAFocMs8nzU621.jpg
下载完成
开始下载: https://www2.autoimg.cn/newsdfs/g30/M01/3C/E2/120x90_0_autohomecar__ChcCSV2BBICAUntfAADjJFd6800429.jpg
下载完成
开始下载: https://www3.autoimg.cn/newsdfs/g26/M0B/3C/65/120x90_0_autohomecar__ChcCP12BFCmAIO83AAGq7vK0sGY193.jpg
下载完成

方式二:基于协程的异步编程实现

import aiohttp
import asyncio
async def fetch(session, url):
    print("发送请求:", url)
    async with session.get(url, verify_ssl=False) as response:
        content = await response.content.read()
        file_name = url.rsplit('_')[-1]
        with open(file_name, mode='wb') as file_object:
            file_object.write(content)
async def main():
    async with aiohttp.ClientSession() as session:
        url_list = [
            'https://www3.autoimg.cn/newsdfs/g26/M02/35/A9/120x90_0_autohomecar__ChsEe12AXQ6AOOH_AAFocMs8nzU621.jpg',
            'https://www2.autoimg.cn/newsdfs/g30/M01/3C/E2/120x90_0_autohomecar__ChcCSV2BBICAUntfAADjJFd6800429.jpg',
            'https://www3.autoimg.cn/newsdfs/g26/M0B/3C/65/120x90_0_autohomecar__ChcCP12BFCmAIO83AAGq7vK0sGY193.jpg'
        ]
        tasks = [asyncio.create_task(fetch(session, url)) for url in url_list]
        await asyncio.wait(tasks)
if __name__ == '__main__':
    asyncio.run(main())

结果
发送请求: https://www3.autoimg.cn/newsdfs/g26/M02/35/A9/120x90_0_autohomecar__ChsEe12AXQ6AOOH_AAFocMs8nzU621.jpg
发送请求: https://www2.autoimg.cn/newsdfs/g30/M01/3C/E2/120x90_0_autohomecar__ChcCSV2BBICAUntfAADjJFd6800429.jpg
发送请求: https://www3.autoimg.cn/newsdfs/g26/M0B/3C/65/120x90_0_autohomecar__ChcCP12BFCmAIO83AAGq7vK0sGY193.jpg
下载完成 https://www2.autoimg.cn/newsdfs/g30/M01/3C/E2/120x90_0_autohomecar__ChcCSV2BBICAUntfAADjJFd6800429.jpg
下载完成 https://www3.autoimg.cn/newsdfs/g26/M0B/3C/65/120x90_0_autohomecar__ChcCP12BFCmAIO83AAGq7vK0sGY193.jpg
下载完成 https://www3.autoimg.cn/newsdfs/g26/M02/35/A9/120x90_0_autohomecar__ChsEe12AXQ6AOOH_AAFocMs8nzU621.jpg

上述两种的执行对比之后会发现,基于协程的异步编程 要比 同步编程的效率高了很多。因为:

  • 同步编程,按照顺序逐一排队执行,如果图片下载时间为2分钟,那么全部执行完则需要6分钟。
  • 异步编程,几乎同时发出了3个下载任务的请求(遇到IO请求自动切换去发送其他任务请求),如果图片下载时间为2分钟,那么全部执行完毕也大概需要2分钟左右就可以了。

小结

协程一般应用在有IO操作的程序中,因为协程可以利用IO等待的时间去执行一些其他的代码,从而提升代码执行效率。

以上就是Python协程的方式实现及意义笔记分享的详细内容,更多关于Python协程的方式实现及意义的资料请关注脚本之家其它相关文章!

相关文章

  • python使用 zip 同时迭代多个序列示例

    python使用 zip 同时迭代多个序列示例

    这篇文章主要介绍了python使用 zip 同时迭代多个序列,结合实例形式分析了Python使用zip遍历迭代长度相等与不等的序列相关操作技巧,需要的朋友可以参考下
    2019-07-07
  • Python接口自动化测试的实现

    Python接口自动化测试的实现

    这篇文章主要介绍了Python接口自动化测试的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • 详解Python中的文件操作

    详解Python中的文件操作

    今天这篇文章给大家介绍Python中的文件操作,文章内容介绍的很详细,有需要的可以参考借鉴。
    2016-08-08
  • 在pandas中遍历DataFrame行的实现方法

    在pandas中遍历DataFrame行的实现方法

    这篇文章主要介绍了在pandas中遍历DataFrame行的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • Linux CentOS7下安装python3 的方法

    Linux CentOS7下安装python3 的方法

    在CentOS7下,默认安装的就是python2.7,下面通过本文给大家分享Linux CentOS7下安装python3 的方法,需要的朋友参考下吧
    2018-01-01
  • python原始套接字编程示例分享

    python原始套接字编程示例分享

    在实验中需要自己构造单独的HTTP数据报文,而使用SOCK_STREAM进行发送数据包,需要进行完整的TCP交互。因此想使用原始套接字进行编程,直接构造数据包,并在IP层进行发送,即采用SOCK_RAW进行数据发送。使用SOCK_RAW的优势是,可以对数据包进行完整的修改,可以处理IP层上的所有数据包,对各字段进行修改,而不受UDP和TCP的限制。
    2014-02-02
  • Python使用tkinter写一个本地密码管理器

    Python使用tkinter写一个本地密码管理器

    闲来无事,看到自己有很多网站的账户密码,有些网站可能打开一两次也就忘记了,下一次在输入账户密码就想不起来,这样很容易丢失账号。所以本文就来用Python和tkinter写一个本地密码管理器吧
    2023-05-05
  • Python字典循环添加一键多值的用法实例

    Python字典循环添加一键多值的用法实例

    今天小编就为大家分享一篇Python字典循环添加一键多值的用法实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-01-01
  • 非递归的输出1-N的全排列实例(推荐)

    非递归的输出1-N的全排列实例(推荐)

    下面小编就为大家带来一篇非递归的输出1-N的全排列实例(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-04-04
  • python实现梯度下降法

    python实现梯度下降法

    这篇文章主要为大家详细介绍了python实现梯度下降法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-03-03

最新评论