首页 > 其他分享 >协程操作

协程操作

时间:2024-01-25 14:58:45浏览次数:16  
标签:__ 协程 函数 async 操作 def asyncio

基于async和await关键字的协程可以实现异步编程,这也是目前Python异步相关的主流技术。

(一)asyncio模块

  • asyncio模块是Python中实现异步的一个模块,该模块在Python3.4的时候发布
  • asycnio和await关键字在Python3.5引入

(二)事件循环

  • 事件循环就是可以把它当作一个while循环,这个while循环在循环生命周期内运行并执行一些任务,在特定的条件下结束循环
  • 获取和创建事件循环的代码:
import asyncio

loop = asyncio.get_event_loop()

(三)协程函数和协程对象

(1)什么是协程函数

  • 使用asycn声明的函数就是协程函数
# 使用 async 声明的函数就是协程函数
async def fn():
    pass

(2)什么是协程对象

  • 协程对象就是协程对象就是调用协程函数之后返回的对象
# 使用 async 声明的函数就是协程函数
async def fn():
    pass


# 调用携程函数得到的对象就是协程对象
res = fn()
print(res) # <coroutine object fn at 0x1029684a0>
  • 注意事项:
    • 调用协程函数时,函数内部的代码不会执行,只是会返回一个协程对象!

(四)协程函数的应用

(1)基本应用

  • 第一种
import asyncio

# 声明一个协程函数
async def func():
    print("协程函数内部的代码")
def main1():
    # 调用协程函数,返回一个协程对象
    res=func()
    # 创建一个事件循环
    loop=asyncio.get_event_loop()
    # 将协程对象当作任务提交到事件循环列表中去,协程执行完毕后终止
    loop.run_until_complete(res)
if __name__ == '__main__':
    main1()
# 协程函数内部的代码
  • 第二种
import asyncio

# 声明一个协程函数
async def func():
    print("协程函数内部的代码")
def main1():
    # 调用协程函数,返回一个协程对象
    res=func()
    # 执行线程对象内部的代码
    asyncio.run(res)
if __name__ == '__main__':
    main1()
# 协程函数内部的代码
  • 步骤:
    • 将协程函数当作任务添加到事件循环的任务列表中
    • 然后事件循环检测列表中的写产生是否已经就绪
    • 准备就绪就执行协程函数内部的代码

(2)await关键字

  • await 是一个只能在协程函数中使用的关键字,用于当协程函数遇到IO操作的时候,挂起当前协程(任务)
  • 当前协程任务被挂起后,事件循环可以去执行其他的协程任务
  • 当前协程IO处理完成是,可以再次切换回来执行await之后的代码

(1)实例1

# 导入模块
import asyncio

# 定义协程函数
async def func():
    print("这是协程内部的代码!!!")

    # 模拟IO阻塞
    # 遇到IO阻塞后,当前协程被挂起,切换到其他的协程去执行# 当IO结束后,切换到当前协程任务
    # 当前的协程被挂起后,事件循环到其他的协程去执行
    res=await asyncio.sleep(3)
    # IO阻塞返回的结果

    print(res)
    print("阻塞完毕后的代码!!!")
def main():
    # 调用协程函数,返回一个协程对象
    res=func()
    # 执行协程函数
    asyncio.run(res)

if __name__ == '__main__':
    main()
# 这是协程内部的代码!!!
# None
# 阻塞完毕后的代码!!!

(2)实例2

# 导入模块
import asyncio
# 定义一个协程函数
async def func():
    print("这是协程内部的代码!!!")
    # 模拟阻塞
    await asyncio.sleep(2)
    print("协程内部代码结束!!!")
    return "返回值"
async def func1():
    print("协程函数func1---里面的代码")
    # 遇到IO操作之后被挂起当前的协程任务,等IO操作之后再去继续执行
    # 当协程被挂起时,事件循环回去执行其他的协程任务
    res= await func()
    print(f"IO请求结束,结果为{res}")
def main():
    # 调用协程函数,得到协程函数对象
    res=func1()
    # 执行协程函数
    asyncio.run(res)
if __name__ == '__main__':
    main()
# 协程函数func1---里面的代码
# 这是协程内部的代码!!!
# 协程内部代码结束!!!
# IO请求结束,结果为返回值

(3)实例3

import asyncio


async def other_tasks():
    print('start')
    await asyncio.sleep(2)  # 模拟遇到了IO操作
    print('end')
    return '返回值'


async def fn():
    print('协程函数内部的代码')

    # 遇到IO操作之后挂起当前协程(任务),等IO操作完成之后再继续往下执行。
    # 当前协程挂起时,事件循环可以去执行其他协程(任务)
    respnse1 = await other_tasks()
    print(f'IO请求结束,结果为:{respnse1}')

    respnse2 = await other_tasks()
    print(f'IO请求结束,结果为:{respnse2}')


def main():
    # 调用协程函数,返回一个协程对象
    cor_obj = fn()

    # 执行协程函数
    asyncio.run(cor_obj)


if __name__ == '__main__':
    main()
    
    '''
    运行结果:
    协程函数内部的代码
    start
    end
    IO请求结束,结果为:返回值
    start
    end
    IO请求结束,结果为:返回值
    '''

(4)小结

  • 上述的实例中之创建了一个任务
    • 事件循环的任务列表中也只有一个任务
    • 无法演示在遇到IO时,切换到其他任务的效果
  • 在程序中要想创建多个任务对象
    • 需要用到Task对象来实现

(3)Task对象

  • Task用于并发调度协程
  • 通过asyncio.create_task(协程对象)的方式创建Task对象
  • 这样除了可以让协程加入事件循环中等待被调度执行
  • 除了使用asyncio.create_task()函数之外
  • 还可以用底层级的loop.create_task()或ensure_future()函数,并且不建议手动实例化Task对象
  • 本质上是将协程对象封装成Task对象
  • 并将协程立即加入事件循环,同时追踪协程的状态
  • 注意事项:
    • asyncio.create_task() 函数在 Python3.7 中被加入。
    • 在 Python3.7 之前,可以改用低层级的
    • asyncio.ensure_future() 函数。

(1)协程运行方式一

# 导入模块
import asyncio

# 定义协程函数
async def func():
    print("这是协程函数内部的代码!!!")
    # 模拟io
    await asyncio.sleep(2)
    print("结束!!!")
    return '返回值'

async  def func1():
    print("这是func1函数内部的代码!!!")
    # 创建协程,将协程封装到一个Task对象中并立即添加到事件循环列表中去,等待事件循环去执行
    task1=asyncio.create_task(func())
    # 创建协程,将协程封装到一个Task对象中并立即添加到事件循环列表中去,等待事件循环去执行
    task2=asyncio.create_task(func())
    # 当执行协程遇到IO后,会自动切换到其他的任务去
    # 此出的await是等待相对应得协程全部执行完毕后获取得结果
    res1=await task1
    res2=await task2

def main():
    # 调用协程对象,返回一个协程对象
    res=func1()
    asyncio.run(res)

if __name__ == '__main__':
    main()
# 这是func1函数内部的代码!!!
# 这是协程函数内部的代码!!!
# 这是协程函数内部的代码!!!
# 结束!!!
# 结束!!!

(2)协程运行方式二

# 导入模块
import asyncio
# 定义协程函数
async def func():
    print("这是协程函数func的内部代码")
    # 模拟io阻塞
    await asyncio.sleep(2)
    print("协程函数func的颞部代码结束")
    return  "返回值"

async def func1():
    print("func1开始!!!")
    # 创建协程,并将协程封装成一个task对象,立即添加到事件循环列表中去,等待事件循环去执行
    task_list=[asyncio.create_task(func()),asyncio.create_task(func())]
    print("func1结束")
    # 当执行某些从遇到IO操作时,会自动切换执行其他的任务
    # 此出的await时等待所以的协程执行完毕,并将所有协程的返回值保存到done
    # 如果设置了timeout值,则意味着最多等待的秒,完成的协程返回值写入到done,未完成的则写道pending
    done,pending=await asyncio.wait(task_list,timeout=None)

    print(f"done---{done}")
    print(f"pending---{pending}")
def main():
    asyncio.run(func1())
if __name__ == '__main__':
    main()
# func1开始!!!
# func1结束
# 这是协程函数func的内部代码
# 这是协程函数func的内部代码
# 协程函数func的颞部代码结束
# 协程函数func的颞部代码结束
# done---{<Task finished name='Task-2' coro=<func() done, defined at D:\old boy\python\python28基础\day42\协程.py:221> result='返回值'>, <Task finished name='Task-3' coro=<func() done, defined at D:\old boy\python\python28基础\day42\协程.py:221> result='返回值'>}
# pending---set()

(3)获取返回值

  • gather 获取协程的返回值
import asyncio


async def other_tasks():
    print('start')
    await asyncio.sleep(2)  # 模拟遇到了IO操作
    print('end')
    return '返回值'


async def fn():
    print('fn开始')

    # 创建协程,将协程封装到一个Task对象中并立即添加到事件循环的任务列表中,等待事件循环去执行(默认是就绪状态)。
    task_lis = [
        asyncio.create_task(other_tasks()),
        asyncio.create_task(other_tasks()),
    ]

    print('fn结束')
    # 当执行某协程遇到IO操作时,会自动化切换执行其他任务。
    # 此处的await是等待所有协程执行完毕,并将所有协程的返回值保存到done
    # 如果设置了timeout值,则意味着此处最多等待的秒,完成的协程返回值写入到done中,未完成则写到pending中。
    await asyncio.wait(task_lis, timeout=None)

    response = await asyncio.gather(task_lis[0], task_lis[1])  # 将task_lis作为参数传入gather,等异步任务都结束后返回结果列表

    print(f'response :>>>> {response}')


def main():
    asyncio.run(fn())


if __name__ == '__main__':
    main()

    '''
    fn开始
    fn结束
    start
    start
    end
    end
    response :>>>> ['返回值', '返回值']
    '''

(4)aiohtpp对象

  • 我们之前学习过爬虫最重要的模块requests,但它是阻塞式的发起请求,每次请求发起后需阻塞等待其返回响应,不能做其他的事情。

    • 本文要介绍的aiohttp可以理解成是和requests对应Python异步网络请求库,它是基于 asyncio 的异步模块,可用于实现异步爬虫,优点就是更快于 requests 的同步爬虫。
    • 安装方式,pip install aiohttp。
  • aiohttp是一个为Python提供异步HTTP 客户端/服务端编程,基于

    asyncio
    

    的异步库。

    • asyncio可以实现单线程并发IO操作,其实现了TCP、UDP、SSL等协议,
    • aiohttp就是基于asyncio实现的http框架。
import aiohttp
import asyncio

async def main():
    async with aiohttp.ClientSession() as session:
        async with session.get("http://httpbin.org/headers") as response:
            print(await response.text())

asyncio.run(main())

(五)异步迭代器

(1)什么是异步迭代器?

  • 实现了 aiter() 和 anext()方法的对象。
  • anext 必须返回一个 awaitable 对象。
  • async for会处理异步迭代器的 anext()方法所返回的可等待对象,直到其引发一个 StopAsyncIteration异常。

(2)什么是异步可迭代对象?

  • 可在 async for语句中被使用的对象。
  • 必须通过它的 aiter()方法返回一个 asynchronous iterator 。
import asyncio


class Reader:
    """ 自定义异步迭代器(同时也是异步可迭代对象) """

    def __init__(self):
        self.count = 0

    async def readline(self):
        self.count += 1
        if self.count == 100:
            return None
        return self.count

    def __aiter__(self):
        return self

    async def __anext__(self):
        val = await self.readline()
        if val is None:
            raise StopAsyncIteration
        return val


async def fn():
    # 创建异步可迭代对象

    async_iter = Reader()
    # async for 必须放在async def 函数内,否则语法错误。
    async for item in async_iter:
        print(item)


asyncio.run((fn()))

(六)异步上下文管理器

  • 此种对象通过定义 __aenter__()__aexit__() 方法来对 async with 语句中的环境进行控制。
import asyncio


class AsyncContextManager:

    def __init__(self):
        self.conn = None

    async def do_something(self):
        # 异步操作数据库
        return 123

    async def __aenter__(self):
        # 异步链接数据库
        self.conn = await asyncio.sleep(1)
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        # 异步关闭数据库链接
        await asyncio.sleep(1)


async def fn():
    async with AsyncContextManager() as f:
        res = await f.do_something()
        print(res)


asyncio.run(fn())

(七)小结

  • 只要看到async 和 await关键字
  • 其内部就是基于协程实现的异步编程
  • 这周异步编程是通过一个线程在IO等待事件去执行其他的协程任务,从而实现并发

标签:__,协程,函数,async,操作,def,asyncio
From: https://www.cnblogs.com/suyihang/p/17987137

相关文章

  • 协程理论
    (一)基于单线程来实现并发(0)并发的本质基于单线程实现并发即只用一个主线程(可利用的cpu只有一个)的情况下实现并发并发的本质:切换+保存状态cpu正在运行一个任务会在两种情况下去执行其他的任务一:发生了IO阻塞二:该任务的计算事件过长或者有一个优先级更高的任务代......
  • python之实现文件增删改查操作
    实现文件增删改查操作         1.写入文件'''以w形式打开文件,写入记录'''1#!/usr/bin/python2withopen('test','w',encoding='utf-8')asf:3'''插入数据'''4f.writelines('......
  • MIT 6.S081入门lab1 操作系统及其接口
    MIT6.S081入门lab1操作系统及其接口一、参考资料阅读与总结1.xv6book书籍阅读(操作系统接口)a.总览操作系统的任务:多个程序之间共享计算机(计算机的硬件管理+任务调度)操作系统接口:使用系统调用,调用内核服务为用户端程序提供给服务(即实现对进程的调度和硬件的管理)操作系统......
  • Linux新手村必备!这些常用操作命令你掌握了吗?
    在计算机的世界里,Linux操作系统以其强大的功能和灵活性受到了广大程序员和IT爱好者的喜爱。然而,对于初学者来说,Linux的操作命令可能会显得有些复杂和难以理解。今天,我们就来一起探索一些Linux常用操作命令,让你的计算机操作更加流畅。一、目录操作首先带大家了解一下Linux系统目......
  • MyBatis手写SQL批量操作
    一、查询List<ArticleAccumulatedIncomeDTO>batchAccumulatedIncome(List<Long>ids);<selectid="batchAccumulatedIncome"resultMap="ArticleSumIncome">selectarticle_id,sum(income)asaccumulated_incomefromwalle......
  • VMware虚拟机安装优麒麟(ubuntukylin)操作系统
    1.镜像下载官网:https://www.ubuntukylin.com/优麒麟官网提供的宣传视频:https://www.ubuntukylin.com/upload/video/202204/1650594049260581.mp4官网提供的视频后续随着版本的更新,此视频可能失效,去官网查看最新的即可,这不是重点1.1搜索出优麒麟官网,下载镜像下载镜像,......
  • 【原创】linux为什么不是实时操作系统
    一、什么是实时操作系统(RTOS)?可参见本博客之前的文章:什么是实时实时的分类常见的RTOSlatency和jitter总结一下,实时其实说的是系统响应事件需要的时间的确定性,时间必须确定,打死都不能超过这个时间。二、linux为什么不是实时操作系统?为了确保系统的实时性,即事件响应产生结果......
  • NanoFramework操作ESP32(一)_基础元器件篇(二十二)_DHT11温湿度传感器
    一、元器件介绍1、针脚用途编号名称功能1VCC电源正2TRIG触发控制信号输入3ECHO回响信号输出4GND电源地2、电气参数 二、示例代码1、代码:元器件的针脚ESP32模块的针脚VCC;供电脚+5VTRIG;发送脚IO17ECHO;接收脚IO16GND......
  • 使用 For each 循环替换xelement操作中断
     使用Foreach循环替换xml元素xelement时,只能操作一个然后就中断了.是因为修改了枚举组自身,导致foreach中断.  解决方案: 在枚举组添加ToArray转换为数组或列表后再进行操作. ForEachbrAsXElementInSelectorXe.......
  • 四、iframe切换+alert切换+鼠标操作+js操作
    1、iframe切换iframe是什么在网页中内嵌了另一个html怎么识别iframeF12打开开发者工具,在Element面板中鼠标点击要操作的元素会显示元素的完整路径,检查里面是否存在iframe,html切换进iframe当中去driver.switch_to.frame(几种方法去......