yield from与协程:委托生成器的工作及停止

 先贴一段《fluent python》上的yield from的经过我修改后的代码,其中修改的部分是为了更好的理解代码运行原理

from collections import namedtuple

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


def averager(key):  # <1>
    total = 0.0
    count = 0
    average = None
    while True:
        print("子生成器运行到这里")
        term = yield 1 # <2>
        if term is None:  # <3>
            print("子生成器收到None")
            break
        print("计算中",key,term)
        total += term
        count += 1
        average = total/count
    return Result(count, average)  # <4>


# the delegating generator
def grouper(results, key):#在伪代码中子生成器抛出异常后并没有yield住,必须向下执行找到yield
    # while True:  # <6>
    #     results[key] = yield from averager(key)  # <7>
    results[key] = yield from averager(key)  # <7>
    print("grouper continue")
    yield 2 #yield一个值方便调用方输出,以便判断过程


# the client code, a.k.a. the caller
def main(data):  # <8>
    results = {}
    for key, values in data.items():
        group = grouper(results, key)  # <9>
        # next(group)  # <10>
        print("预激值",group.send(None))
        for value in values:
            group.send(value)  # <11>
            # print(group.send(value))
        print("-----------------one over:",group.send(None))  # important! <12>
    # print(results)  # uncomment to debug
    report(results)


# output report
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)

 运行结果

子生成器运行到这里
预激值 1         #调用方send(None)经由委托生成器send给了子生成器并返回yield数值(为1)
计算中 girls;kg 40.9
子生成器运行到这里
计算中 girls;kg 38.5
子生成器运行到这里
计算中 girls;kg 44.3
·····省略·······
··············
子生成器运行到这里
计算中 girls;kg 44.5
子生成器运行到这里   #退出前仍要运行到下一次yield,所以打印提示
子生成器收到None  #子生成器退出
grouper continue #委托生成器在子生成器return(报错)后仍要向下执行到yield 2,故打印
-----------------one over: 2    #一次子生成器任务结束,委托生成器关闭并打印提示
子生成器运行到这里
预激值 1
计算中 girls;m 1.6
子生成器运行到这里
计算中 girls;m 1.51
子生成器运行到这里
················
····省略········
···············
-----------------one over: 2
 9 boys  averaging 40.42kg
 9 boys  averaging 1.39m
10 girls averaging 42.04kg
10 girls averaging 1.43m

yield from运行过程的伪代码及解读: 

# """
# _i:子生成器,同时也是一个迭代器
# _y:子生成器生产的值
# _r:yield from 表达式最终的值
# _s:调用方通过send()发送的值
# _e:异常对象
#
# """
#
# _i = iter(EXPR)      # EXPR:可迭代对象,_i:子生成器;
# try:
#     _y = next(_i)   # 预激子生成器,把产出的第一个值存在_y中;
# except StopIteration as _e:
#     _r = _e.value   # 如果抛出了`StopIteration`异常,那么就将异常对象的`value`属性保存到_r,这是最简单的情况的返回值;
# else:
#     while 1:    # 尝试执行这个循环,委托生成器会阻塞;
#         _s = yield _y   # 生产子生成器的值,等待调用方`send()`值,发送过来的值将保存在_s中;
#         try:
#             _y = _i.send(_s)    # 转发_s,并且尝试向下执行;
#         except StopIteration as _e:
#             _r = _e.value       # 如果子生成器抛出异常,那么就获取异常对象的`value`属性存到_r,退出循环,恢复委托生成器的运行;
#             break
# RESULT = _r     # 获取因子生成器抛出StopIteration 而返回的值
#注意调用方的send并没有在委托生成器中执行到yield,所以仍会向下执行,
#在原代码中使用while True可以解决这一问题但可能会让读者忽略重要信息

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值