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

2018-09-25  本文已影响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返回。

生成器的注意事项?

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」-协程的工作流程

上一篇下一篇

猜你喜欢

热点阅读