协程系列之yield与yield from

286 阅读2分钟

我们可以认为一个线程是调用某个函数方法,协程可以控制函数方法的执行过程,转向其他函数方法,并在适当的时候切换到原来的函数方法中继续执行。python中常见的协程模块有yield、yield from、async/wait、asyncio、Gevent等。只有Gevent是第三方模块,其他都是python的内置模块

1. yield

def consumer():
    """
    任务1:接收数据,处理数据
    :return:
    """
    print('开始接受数据')
    while True:
        print('等待中')
        x = yield
        print('处理数据:', x)
​
​
def producer():
    """
    任务2:生产数据
    :return:
    """
    c = consumer()
    # 找到consumer()的yield位置
    next(c)
    for i in range(2):
        print('发送数据:', i)
        c.send(i)
​
​
if __name__ == '__main__':
    producer()

result:

开始接受数据
等待中
发送数据: 0
处理数据: 0
等待中
发送数据: 1
处理数据: 1
等待中

分析过程如下:

(1)当我们调用producer方法的时候,方法中调用了consumer方法,并生成了方法对象c

(2)consumer方法处于死循环状态。如果按照正常的调用方式,它会使producer方法处于死循环。但通过使用yield可以将consumer方法和producer方法相互切换

(3)当consumer方法的对象c调用send方法的时候,程序从producer方法切换到consumer方法的yield位置开始执行consumer方法

(4)由于consumer方法处于死循环状态,当再次执行到yield位置的时候,代表consumer方法已执行完成,程序会自动切换到producer方法继续执行

2. yield from

从3.3版本开始,引入了yield from语句。它不仅简化了yield多层嵌套的代码,还弥补了yield的不足。其语法如下:

# iterable为可迭代对象,如列表、元组、生成器等
yield from iterable为可迭代对象,如列表、元组、生成器等

该语法等同于下面的代码:

for item in iterable:
    yield item

yield from重要的作用是提供了一个数据传输管道

def consumer():
    """
    任务1:接收数据,处理数据
    :return:
    """
    print('开始接受数据')
    while True:
        print('等待中')
        x = yield
        print('处理数据:', x)
​
​
def wraps(c):
    while True:
        print('running')
        yield from c
​
​
def producer(wrap):
    """
    任务2:生产数据
    :return:
    """
    next(wrap)
    for i in range(2):
        print('发送数据:', i)
        wrap.send(i)
​
​
if __name__ == '__main__':
    c = consumer()
    wrap = wraps(c)
    producer(wrap)

result:

running
开始接受数据
等待中
发送数据: 0
处理数据: 0
等待中
发送数据: 1
处理数据: 1
等待中

producer方法和consumer方法之间开始是通过wraps方法传递函数对象的,但是wraps方法只是为producer方法和consumer方法提供一个数据传输管道,不参与producer方法和consumer方法之间的数据通信。