yield-from示例

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time    : 2018/6/20 9:13
# @File    : yield_from11.py


from collections import namedtuple

Result = 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.00kg
6 boys  averaging 1.68m
6 girls averaging 44.00kg
6 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 异常。

原文地址:https://www.cnblogs.com/fmgao-technology/p/9202561.html