#!/usr/bin/python3
# -*- coding: utf-8 -*-# @Time : 2018/6/20 9:13# @File : yield_from11.pyfrom collections import namedtupleResult = namedtuple('Result', 'count average')# 子生成器def averager(): total = 0.0 count = 0 average = None while True: # main 函数发送数据到这里 term = yield if term is None: # 终止条件 break total += term count += 1 average = total / count return Result(count, average) # 返回的Result 会成为grouper函数中yield from表达式的值# 委派生成器def grouper(results, key): # 这个循环每次都会新建一个averager实例,每个实例都是作为协程使用的生成器对象 while True: # grouper 发送的每个值都会经由yield from 处理,通过管道传给 averager # grouper 会在yield from表达式处暂停,等待 averager 实例处理客户端发来的值。 # averager 实例运行完毕后,返回的值绑定到results[key] 上。 # while 循环会不断创建 averager 实例,处理更多的值。 results[key] = yield from averager()# 调用方def main(data): results = {} for key, values in data.items(): # group 是调用grouper函数得到的生成器对象,传给grouper 函数的第一个参数是results,用于收集结果;第二个是某个键 group = grouper(results, key) next(group) for value in values: # 把各个value传给grouper 传入的值最终到达averager函数中 # grouper并不知道传入的是什么,同时grouper实例在yield from处暂停 group.send(value) # 把None传入groupper,传入的值最终到达averager函数中,导致当前实例终止。然后继续创建下一个实例。 # 如果没有group.send(None),那么averager子生成器永远不会终止,委派生成器也永远不会在此激活,也就不会为result[key]赋值 group.send(None) 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)) print('{:2} {:5} averaging {:.2f}{}'.format(result.count, group, result.average, unit)) # print(result, type(result)) # Result(count=6, average=54.0) <class '__main__.Result'>data = { 'girls;kg': [40, 41, 42, 43, 44, 54], 'girls;m': [1.5, 1.6, 1.8, 1.5, 1.45, 1.6], 'boys;kg': [50, 51, 62, 53, 54, 54], 'boys;m': [1.6, 1.8, 1.8, 1.7, 1.55, 1.6],}if __name__ == '__main__': main(data)''' 这段代码从一个字典中读取男生和女生的身高和体重。然后把数据传给之前定义的 averager 协程,最后生成一个报告。执行结果为6 boys averaging 54.00kg6 boys averaging 1.68m6 girls averaging 44.00kg6 girls averaging 1.58m这断代码展示了yield from 结构最简单的用法。委派生成器相当于管道,所以可以把任意数量的委派生成器连接在一起---一个委派生成器使用yield from 调用一个子生成器,而那个子生成器本身也是委派生成器,使用yield from调用另一个生成器。最终以一个只是用yield表达式的生成器(或者任意可迭代对象)结束。'''yield from 的意义PEP380 分6点说明了yield from 的行为。 子生成器产出的值都直接传给委派生成器的调用方(客户端代码) 使用send() 方法发给委派生成器的值都直接传给子生成器。如果发送的值是None,那么会调用子生成器的 __next__()方法。如果发送的值不是None,那么会调用子生成器的send()方法。如果调用的方法抛出StopIteration异常,那么委派生成器恢复运行。任何其他异常都会向上冒泡,传给委派生成器。 生成器退出时,生成器(或子生成器)中的return expr 表达式会触发 StopIteration(expr) 异常抛出。 yield from表达式的值是子生成器终止时传给StopIteration异常的第一个参数。 传入委派生成器的异常,除了 GeneratorExit 之外都传给子生成器的throw()方法。如果调用throw()方法时抛出 StopIteration 异常,委派生成器恢复运行。StopIteration之外的异常会向上冒泡。传给委派生成器。 如果把 GeneratorExit 异常传入委派生成器,或者在委派生成器上调用close() 方法,那么在子生成器上调用close() 方法,如果他有的话。如果调用close() 方法导致异常抛出,那么异常会向上冒泡,传给委派生成器;否则,委派生成器抛出 GeneratorExit 异常。