协程中的委派生成器如何停止循环?

本人最近在学习Python中的协程知识。在感叹yield from构造的委派生成器能够连通子生成器和待处理数据的同时,遇到一处细节苦思不解,下面详述我的问题。

问题描述:
在下面的实例中,我们需要利用委派生成器的知识将某班级的男女生身高和体重分别计算平均值并输出。下面的代码是可以算出正确结果的。

但是我不理解,委派生成器(就是averager())在每次传输给子生成器数据的并接受子生成器反馈的过程中,对应的while True循环是如何停止并进入新一轮平均值的计算的呢?是因为子生成器break的影响吗?

希望明白的前辈帮忙指点,感激不尽!!!

from collections import namedtuple

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

def subaverager():
    total = 0.0
    number = 0
    average = None
    while True:
        term = yield
        if term is None:
            break
        total += term
        number += 1
        average=total/number
    return Result(average,number)

def averager(results,key):
    while True:
        results[key]=yield from subaverager()

def main(grouper):
    results={}
    for key,group in grouper.items():
        term = averager(results,key)
        next(term)
        for value in group:
            term.send(value)
        term.send(None)
    result(results)
    
def result(results):
    for key,value in results.items():
        gender,unit=key.split(';')
        print('{} {} averaging {:.2f} {}.'.format(
            value.number,gender,value.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)

输出如下:

10 girls averaging 42.04 kg.
10 girls averaging 1.43 m.
9 boys averaging 40.42 kg.
9 boys averaging 1.39 m.
阅读 3k
1 个回答

averager的while True并没有停止,而是main的for循环停止了,所以没有在next(term)。averager因为while true生成器不会停止,你可以一直next(term)。新的一轮计算是因为term = averager(results,key)又构造了一个新的生成器。

去掉while true版本,有值啊,你怎么写的?

from collections import namedtuple

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

def subaverager():
    total = 0.0
    number = 0
    average = None
    while True:
        term = yield
        if term is None:
            break
        total += term
        number += 1
        average=total/number
    return Result(average,number)

def averager(results,key):
    results[key]=yield from subaverager()

def main(grouper):
    results={}
    for key,group in grouper.items():
        term = averager(results,key)
        next(term)
        for value in group:
            term.send(value)
        try:
            term.send(None)
        except StopIteration:
            pass
    result(results)
    
def result(results):
    for key,value in results.items():
        gender,unit=key.split(';')
        print('{} {} averaging {:.2f} {}.'.format(
            value.number,gender,value.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)
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏