先贴一段《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可以解决这一问题但可能会让读者忽略重要信息