Python 8 协程

1、用yield关键字和send方法实现简单的协程

def simple_coroutine():                 # 携程使用生成器函数定义:定义题中有yield关键字
    print('-> coroutine started')       # 如果携程只从客户那里接受数据,那么产出的值是None,这个值是隐式的,因为yield关键字右边没有表达式
    x = yield
    print('-> coroutine received:', x)

my_coro = simple_coroutine()
print(my_coro)                          # 与创建生成器的方式一样,调用函数得到生成器对象
next(my_coro)                           # 首先要调用next(..)函数,因为生成器还没有启动,没在yield语句初暂停,所以一开始无法发送数据

my_coro.send(10)                        # 调用这个方法后,携程定义中的yield表但是会出现10,直到下一个yield出现或者终止

以上代码结果为:

<generator object simple_coroutine at 0x102a463b8>
-> coroutine started
-> coroutine received: 10
Traceback (most recent call last):
    ...........                
    my_coro.send(10)                        # 调用这个方法后,携程定义中的yield表但是会出现10,直到下一个yield出现或者终止
StopIteration

如果yield未返回实际数据,则会返回None。send方法只可以把值赋给yield表达式,所以只有在协程暂停在yield的时候才可以send值,因此,必须先使用next激活协程,才可以send值,这一步称为“预激”。

 

2、使用协程计算平均值

from collections import namedtuple
from functools import wraps

Result = namedtuple('Result', 'count average')

def coroutine(func):
    @wraps(func)
    def inner(*args, **kwargs):
        gen = func(*args, **kwargs)
        next(gen)
        return gen
    return inner

@coroutine
def averager():
    total = 0  # 总数
    count = 0  # 总个数
    average = None  # 平均值
    while True:
        term = yield
        if term is None:
            break
        total += term
        count += 1
        average = total / count
    return Result(count, average)

a = averager()
a.send(1)
try:
    a.send(None)
except StopIteration as e:
    result = e.value
    
print(result)

 

3.yield from关键字

yield from 可以用来简化for循环:

def gen():
    yield from 'AB'
    yield from range(10)

用yield from当做委派生成器,这样就可以让多个任务并发的去处理,而不需要等待上一个任务完成。

from collections import namedtuple


Result = namedtuple('Result', 'count average')


# 子生成器
def averager():                                 # 子生成器
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield                            # 通过main函数中的gourp.send()接收到term的值
        if term is None:                        # 至关重要的终止条件,告诉协程所有的数据已经结束,结束协程
            break
        total += term
        count += 1
        average = total/count
    return Result(count, average)               # 返回 grouper 中yield from的值


# 委派生成器
def grouper(results, key):                      # 委派生成器
    while True:                                 # 每次循环都会创建一个averager的实例
        results[key] = yield from averager()    # grouper发送的每个值都会让yield from处理,把产出的值绑定给resuluts[key]


# 客户端代码,即调用方
def main(data):                                 # main函数是客户端代码
    results = {}
    for key, values in data.items():
        group = grouper(results, key)           # group是调用grouper的生成器
        next(group)                             # 预激group协程
        for value in values:
            group.send(value)                   # 把各个value的值传递给grouper,通过grouper传入averager中term
        group.send(None)                        # 所有值传递结束以后,终止averager
    #print(results)                             # 如果要调试,去掉注释
    report(results)

#输出报告
def report(results):
    for key, result in sorted(results.items()):
        group, unit = key.split(';')
        print('{:2} {:5} averaging {:.2f}{}'.format(
              result.count, group, result.average, unit))


data = {
    'girls;kg':
        [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5],
    'girls;m':
        [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43],
    'boys;kg':
        [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],
    'boys;m':
        [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46],
}

if __name__ == '__main__':
    main(data)

 

posted @ 2019-05-11 01:12  不可思议的猪  阅读(251)  评论(0编辑  收藏  举报