• 协程


    一协程

    二爬虫案例

    三 异步编程

    一 协程

    协程也可以被称为微线程,是一种用户态内的上下文切换技术。简而言之,其实就是 通过一个线程实现代码块相互切换执行。例如:

    示例

    def func1():
        print(1)
        print(2)
    
    
    def func2():
        print(3)
        print(4)
        
        
        
    func1()
    func2()

    如果我们执行上面的代码就会执行出来的结果是 1 , 2, 3, 4 但是如果协程接入的话 就出现了 1, 3, 2, 4

    协程的实现

    在Python中有多种方式可以实现协程,例如:

    1 greenlet,是一个第三方模块,基于实现协程代码(Gevent协程就是基于greenlet实现)

    2 yield,生成器,借助生成器的特点也可以实现协程代码。

    3 asyncio,在Python3.4中引入的模块用于编写协程代码。

    4 async & awit,在Python3.5中引入的两个关键字,结合asyncio模块可以更方便的编写协程代码。

    举例:

    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 函数

    yield

    基于Python的生成器的yield和yield form关键字实现协程代码。
    def func1():
        yield 1
        yield from func2()
        yield 2
    
    
    def func2():
        yield 3
        yield 4
    
    
    f1 = func1()
    for item in f1:
        print(item)

    asyncio

    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))

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

    async & awit

    async & awit 关键字在Python3.5版本中正式引入,基于他编写的协程代码其实就是 上一示例 的加强 版,让代码可以更加简便。 Python3.8之后 @asyncio.coroutine 装饰器就会被移除,推荐使用sync & 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))

    二 爬虫案例

    例如:用代码实现下载 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)

    方式二 异步

    """
    下载图片使用第三方模块aiohttp,请提前安装:pip3 install aiohttp
    """
    # !/usr/bin/env python
    # -*- coding:utf-8 -*-
    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())

    三 异步编程 

    基于 async & await 关键字的协程可以实现异步编程,这也是目前python异步相关的主流技术。 想要真正的了解Python中内置的异步编程,根据下文的顺序一点点来看

    3.1 事件循环

    事件循环,可以把他当做是一个while循环,这个while循环在周期性的运行并执行一些 任务 ,在特定条 件下终止循环

    import asyncio
    loop = asyncio.get_event_loop()

    3.2 协程和异步编程

    协程函数,定义形式为 async def 的函数。

    协程对象,调用协程函数 所返回的对象。

    # 定义一个协程函数
    async def func():
       pass

    # 调用协程函数,返回一个协程对象 result = func()

    # 注意:调用协程函数时,函数内部代码不会执行,只是会返回一个协程对象

    3.2.1 基本应用

    程序中,如果想要执行协程函数的内部代码,需要 事件循环协程对象 配合才能实现,如:

    import asyncio
    
    
    async def func():
        print("协程内部代码")
    
    
    # 调用协程函数,返回一个协程对象。
    result = func()
    # 方式一
    # loop = asyncio.get_event_loop() # 创建⼀个事件循环
    # loop.run_until_complete(result) # 将协程当做任务提交到事件循环的任务列表中,协程执行完成之后终止。

    # 方式二 # 本质上方式一模一样的,内部先 创建事件循环 然后执行 run_until_complete,一个简便的写法。 # asyncio.run 函数在 Python 3.7 中加入 asyncio 模块, asyncio.run(result)

    3.2.2 await

    【await+可等待对象(协程对象,Future,Task对象)】

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

    示例1

    import asyncio
    
    
    async def func():
        print("执⾏协程函数内部代码")
        # 遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执⾏。
        # 当前协程挂起时,事件循环可以去执⾏其他协程(任务)。
        response = await asyncio.sleep(2)
        print("IO请求结束,结果为:", response)
    
    
    result = func()
    
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(result)
    
    # asyncio.run(result)

    示例2

    import asyncio
    
    
    async def others():
        print("start")
        await asyncio.sleep(2)
        print('end')
        return '返回值'
    
    
    async def func():
        print("执行协程函数内部代码")
        # 遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执行。当前协程挂起时,事件循环可以去执行其他协程(任务)。
        response = await others()
        print("IO请求结束,结果为:", response)
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(func())
    # asyncio.run(func())

    示例3 

    import asyncio
    
    
    async def others():
        print("start")
        await asyncio.sleep(2)
        print('end')
        return '返回值'
    
    
    async def func():
        print("执行协程函数内部代码")
        # 遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执行。当前协程挂起时,事件循环可以去执行其他协程(任务)。
        response1 = await others()
        print("IO请求结束,结果为:", response1)
    
        response2 = await others()
        print("IO请求结束,结果为:", response2)
    
    # asyncio.run(func())
    loop = asyncio.get_event_loop()
    loop.run_until_complete(func())

    上述的所有示例都只是创建了一个任务,即:事件循环的任务列表中只有一个任务,所以在IO等待时 无法演示切换到其他任务效果。 在程序想要创建多个任务对象,需要使用Task对象来实现。

    3.2.3 Task对象

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

    注意: asyncio.create_task() 函数在 Python 3.7 中被加入。在 Python 3.7 之前,可以改用低层级 的 asyncio.ensure_future() 函数

    import asyncio
    
    
    async def func():
        print(1)
        await asyncio.sleep(2)
        print(2)
        return "返回值"
    
    
    async def main():
        print("main开始")
        # 创建协程,将协程封装到⼀个Task对象中并立即添加到事件循环的任务列表中,等待事件循环去执行(默认是就绪状态)。
        task1 = asyncio.create_task(func())
        # 创建协程,将协程封装到⼀个Task对象中并立即添加到事件循环的任务列表中,等待事件循环去执行(默认是就绪状态)。
        task2 = asyncio.create_task(func())
        print("main结束")
        # 当执行某协程遇到IO操作时,会自动化切换执行其他任务。
        # 此处的await是等待相对应的协程全都执行完毕并获取结果
        ret1 = await task1
        ret2 = await task2
        print(ret1, ret2)
        
    asyncio.run(main())
  • 相关阅读:
    HDU 3951 (博弈) Coin Game
    HDU 3863 (博弈) No Gambling
    HDU 3544 (不平等博弈) Alice's Game
    POJ 3225 (线段树 区间更新) Help with Intervals
    POJ 2528 (线段树 离散化) Mayor's posters
    POJ 3468 (线段树 区间增减) A Simple Problem with Integers
    HDU 1698 (线段树 区间更新) Just a Hook
    POJ (线段树) Who Gets the Most Candies?
    POJ 2828 (线段树 单点更新) Buy Tickets
    HDU 2795 (线段树 单点更新) Billboard
  • 原文地址:https://www.cnblogs.com/a438842265/p/12797275.html
Copyright © 2020-2023  润新知