美文网首页
「Python异步编程-3」协程、生成器、yield 的联系

「Python异步编程-3」协程、生成器、yield 的联系

作者: 言淦 | 来源:发表于2018-09-25 09:48 被阅读0次

    异步编程的基础在于理解协程,而协程的基础在于理解生成器,而生成器的基础在于理解yield关键字,下面就来说说这几个概念。

    什么是yield关键字?
    相当于return关键字,在每次next(),或者for遍历的时候,都会yield这里将新的值返回回去,并在这里阻塞,等待下一次的调用

    什么是生成器?
    每次触发只会返回一个值,用next(generator) 和generator.send() 触发,比如下面这个例子。

    from collections.abc import Generator
    
    def target(N):
        count = 0
        while count < N:
            yield count
            count += 1
    
    tar = target(5)
    print(isinstance(tar, Generator))  # True
    print(next(tar))   # 0, 因为一开始count=0, 执行到yield count就把count返回,所以会打印 0
    print(tar.send(None))  # 1, 执行完该语句后,会跳转到yield count;并继续执行count+=1, 所以count就变成1;再执行yield count, 将1返回。
    

    生成器的注意事项?

    • 注意生成器的实例个数
    • 注意send()方法传递的参数
      • 当参数为None时, 与next(generator)等价
      • 当参数为数字时,会通过yield关键字传递到生成器

    talk is cheap,show me the code!

    # 第一个注意事项,生成器的实例个数(与之前的代码进行比较)
    from collections.abc import Generator
    
    def target(N):
        count = 0
        while count < N:
            yield count
            count += 1
    
    print(isinstance(target(5), Generator))  # True
    print(next(target(5)))  # 0 
    print(target(5).send(None)) # 0,这里返回0是相当于创建一个新的生成器实例,所以第一个值返回的肯定是0
    
    # 第二个注意事项,send方法传递的参数
    # 1.jump = yield index 应该拆分为两个语句,分别是 jump = yield 和 yield(return) index;
    # 2.jump = yield接收通过send()发送的信息,并赋值给jump;yield index 是将index return给外部程序。
    
    def jumping_range(N):
        index = 0
        while index < N:
            jump = yield index
            if jump is None:
                jump = 1
            index += jump
    
    if __name__ == '__main__':
        itr = jumping_range(5)
        print(itr.send(None)) # 1
        print(next(itr))  # 2
        print(itr.send(2))  # 3, 这条语句不能放到位置1, 具体可以自己尝试
    

    想必到这里大家应该对生成器和yield有个了解,那什么是协程呢?

    协程:
    网上常见说法是轻量线程,它的关系类似与线程与进程的关系,但是它不用考虑线程安全。
    我的理解是协程是程序之间协作配合,或者说是不同函数之间的配合,因为它的级别只是函数,所以它在单个线程内,也就不同考虑线程安全。而且如果我们把每个函数抽象为一个任务,其实协程实现了单线程多任务,这也是它的一个亮点之一,提升了效率。

    这段话有两个比较难理解的,一是函数怎么配合?二是效率怎么提升?

    # 在这段程序中,await asyncio.sleep(x) 会阻塞函数相应的秒数,如果按照同步的方式,总共应该是6秒,而通过协程,其实实现了异步,时间变为4秒!
    
    import asyncio
    import time
    
    start = time.time()
    # 协程函数
    async def do_some_work(x):
        print('Waiting: ', x)
        await asyncio.sleep(x)
        return 'Done after {}s'.format(x)
    
    # 协程对象
    coroutine1 = do_some_work(2)
    coroutine3 = do_some_work(4)
    
    # 将协程转成task,并组成list
    tasks = [
        asyncio.ensure_future(coroutine1),
        asyncio.ensure_future(coroutine3)
    ]
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
    
    for task in tasks:
        print('Task ret: ', task.result())
    
    print("time_consume:", time.time() - start)
    
    ----------
    Waiting:  2
    Waiting:  4
    Task ret:  Done after 2s
    Task ret:  Done after 4s
    time_consume: 4.002617359161377
    

    「Python异步编程-先吃个面包」就写到这里,希望你们读了之后能有面包那种饱腹感我就心满意足了。

    其实关于yield,生成器,协程有很多概念没将明白,只是抽出了自己觉得比较重要的,更加全面的教程可以看这里(良心推荐):
    Python进阶开发之路

    另外,廖雪峰大大的教程也可以作为辅助性教程

    「Python异步编程-1」总览全局
    「Python异步编程-2」可迭代对象、迭代器、生成器
    「Python异步编程-3」协程、生成器、yield 的联系
    「Python异步编程-4」-协程的工作流程

    相关文章

      网友评论

          本文标题:「Python异步编程-3」协程、生成器、yield 的联系

          本文链接:https://www.haomeiwen.com/subject/bsvcoftx.html