用法一:
# BEGIN YIELD_FROM_AVERAGER
from collections import namedtuple
Result = namedtuple('Result', 'count average')
# 子生成器
def averager(): # <1>
total = 0.0
count = 0
average = None
while True:
term = yield # <2>
if term is None: # <3>
break
total += term
count += 1
average = total/count
return Result(count, average) # <4>
# 委派生成器
def grouper(results, key): # <5>
while True: # <6>
results[key] = yield from averager() # <7>
# 客户端代码, 即调用方
def main(data): # <8>
results = {} # 可变类型:如**列表(list)、字典(dict)**。如fun(a),在函数内部修改a的值,影响函数外的a本身。
for key, values in data.items():
group = grouper(results, key) # <9>
next(group) # <10>
for value in values:
group.send(value) # <11>
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)
# END YIELD_FROM_AVERAGER
运行结果:
9 boys averaging 40.42kg
9 boys averaging 1.39m
10 girls averaging 42.04kg
10 girls averaging 1.43m

-
yield from的主要功能是打开双向通道,把最外层的调用方与最内层的子生成器连接起来,这样二者可以直接发送和产出值,还可以直接传入异常,而不用在位于中间的协程中添加大量处理异常的样板代码。有了这个结构,协程可以通过以前不可能的方式委托职责。
-
程序在执行到next(group)时,预激委派生成器grouper,进入while True循环,调用子生成器averager后,在yield from表达式处暂停。
-
内层for调用group.send(value),直接把值传给子生成器averager.同时,当前的grouper实例(gruop)在yield from表达式处暂停。
-
group.send(value)将值传送给子生成器 term = yield这一行,委派生成器永远不知道传入的值。(yield from后面的子生成器会自动预激)
-
group.send(None)终止averager子生成器,返回results[key]值给委派生器,赋值给results[key]。
-
如果子生成器不终止,委派生成器会在yield from表达式处永远暂停,这时程序不会向前执行,因为yield from把控制权转交给客户代码(调用方)。
-
激活协程可调用next(group),也可以调用group.send(None),效果一样。
用法二:
用于简化for循环中的yield表达式
def gen():
for c in 'AB':
yield c
for i in range(1,3):
yield i
print(list(gen()))
输出:
['A', 'B', 1, 2]
改成yield from:
def gen():
yield from 'AB'
yield from range(1,3)
print(list(gen()))
输出:
['A', 'B', 1, 2]
以上就是yield from的用法。yield from后面可接可迭代对象,可以把可迭代对象里的每个元素一个一个的yield出来,对比yield来说代码更加简洁,结构更加清晰。yield from后面可接一个生成器函数。
网友评论