本人最近在学习Python中的协程知识。遇到一处委派生成器利用while True处理子生成器的StopIteration并获取异常附带的值的代码例子。
代码实例问题描述:
在下面的实例中,我们需要利用委派生成器的知识将某班级的男女生身高和体重分别计算平均值并输出。第一段代码是可以算出正确结果的。
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)
print(results)#调试使用
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)
正确输出如下:
{'girls;kg': Result(average=42.040000000000006, number=10)}
{'girls;kg': Result(average=42.040000000000006, number=10), 'girls;m': Result(average=1.4279999999999997, number=10)}
{'girls;kg': Result(average=42.040000000000006, number=10), 'girls;m': Result(average=1.4279999999999997, number=10), 'boys;kg': Result(average=40.422222222222224, number=9)}
{'girls;kg': Result(average=42.040000000000006, number=10), 'girls;m': Result(average=1.4279999999999997, number=10), 'boys;kg': Result(average=40.422222222222224, number=9), 'boys;m': Result(average=1.3888888888888888, number=9)}
10 girls averaging 42.04 kg.
10 girls averaging 1.43 m.
9 boys averaging 40.42 kg.
9 boys averaging 1.39 m.
在第一轮平均值的计算中,当主函数传给子生成器None使其终止时,子生成器会把StopIteration以及第一轮计算结果的值返回给委派生成器(就是averager())。我自己写上述代码时没有像正确代码一样,在委派生成器中加入while True。我写的averager()如下:
def averager(results,key):
results[key]=yield from subaverager()#自己写委派生成器没有加while True
如果不添加While True,解释器报错StopIteration,主函数的print(results)也不会输出任何字符串。相反,如果添加while True,代码能够顺利运行。
所以我的理解是,委派生成器中的while True能够处理StopIteration并将异常的值赋给results[key],然后在下一个循环的yield from处阻塞。我不清楚这么理解对不对
,恳请明白的前辈帮我指点迷津,感激不尽!!!
不添加While True 我猜你是不是也没有执行term.send(None),执行send(None)可以拿到正确的结果
没有send(None)的话subaverager的while True没有跳出,所以还没有运行到return,averager的yield from没有等到返回值,当然不会给results[key]赋值,所以results还是空dict
看这样你能理解么, 加上while True,term.send(None)后results[key]获得了数据,之后又运行yield from 等待下一个值,所以averager生成器没有抛出StopIteration,下一个值我特意减去了第一个人,你会发现前一个值被冲掉了,就是字典赋值的证据
while True也可以换成yield,看看下面这个列子