yield 与 yield from
简而言之yield from 就是把main里的send数据 传入yield处, send(None)的功能近乎于next(w)
def test1(): while True: yield from test2() def test2(): while True: y = yield if y == None: break def main(): w = test1() next(w) for i in range(0, 10): w.send(i) w.send(None)
main()
三者之间的关系图
委派生成器在 yield from 表达式处暂停时,调用方可以直接把数据发给子生成器,子生成器再把产出的值发给调用方。子生成器返回之后,解释器会抛出StopIteration 异常,并把返回值附加到异常对象上,此时委派生成器会恢复。
grouper 发送的每个值都会经由 yield from 处理,通过管道传给 averager 实例。grouper 会在 yield from 表达式处暂停,等待 averager 实例处理客户端发来的值。averager 实例运行完毕后,返回的值绑定到 results[key] 上。while 循环会不断创建 averager 实例,处理更多的值。
外层 for 循环重新迭代时会新建一个 grouper 实例,然后绑定到 group 变量上。前一个 grouper 实例(以及它创建的尚未终止的 averager 子生成器实例)被垃圾回收程序回收。
from collections import namedtuple Result = namedtuple('Result', 'count average') # 子生成器 def averager(): total = 0.0 count = 0 average = None while True: # main 函数发送数据到这里 print("in averager, before yield") term = yield if term is None: # 终止条件 break total += term count += 1 average = total/count print("in averager, return result") return Result(count, average) # 返回的Result 会成为grouper函数中yield from表达式的值 # 委派生成器 def grouper(results, key): # 这个循环每次都会新建一个averager 实例,每个实例都是作为协程使用的生成器对象 while True: print("in grouper, before yield from averager, key is ", key) results[key] = yield from averager() print("in grouper, after yield from, key is ", key) # 调用方 def main(data): results = {} for key, values in data.items(): # group 是调用grouper函数得到的生成器对象 group = grouper(results, key) print("\ncreate group: ", group) next(group) #预激 group 协程。 print("pre active group ok") for value in values: # 把各个value传给grouper 传入的值最终到达averager函数中; # grouper并不知道传入的是什么,同时grouper实例在yield from处暂停 print("send to %r value %f now"%(group, value)) group.send(value) # 把None传入groupper,传入的值最终到达averager函数中,导致当前实例终止。然后继续创建下一个实例。 # 如果没有group.send(None),那么averager子生成器永远不会终止,委派生成器也永远不会在此激活,也就不会为result[key]赋值 print("send to %r none"%group) group.send(None) print("report result: ") 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, 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)