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)