下面示例 是 averager 协程的不同版本,这一版会返回结果。为了说明如何返回值,每次激活协程时不会产出移动平均值。这么做是为了强调某些协程不会产出值,而是在最后返回一个值(通常是某种累计值)
"""
让协程返回值
"""
from collections import namedtuple
Result = namedtuple('Result', 'count average')
def averager():
total = 0.0
count = 0
average = None
while True:
term = yield
if term is None:
# 为了返回值,协程必须正常终止;因此,这一版 averager 中有个条件判断,以便退出
# 累计循环。
break
total += term
count += 1
average = total / count
# 返回一个 namedtuple,包含 count 和 average 两个字段。在 Python 3.3 之前,如果生成
# 器返回值,解释器会报句法错误。
return Result(count, average)
if __name__ == '__main__':
coro_avg = averager()
next(coro_avg)
coro_avg.send(10) # 这一版不产出值。
coro_avg.send(30)
coro_avg.send(40)
coro_avg.send(80)
coro_avg.send(10.5)
try:
# 发送 None 会终止循环,导致协程结束,返回结果。一如既往,生成器对象会抛出
# StopIteration 异常。异常对象的 value 属性保存着返回的值。
coro_avg.send(None)
except StopIteration as e:
result = e.value
print(result)
# Result(count=5, average=34.1)
获取协程的返回值虽然要绕个圈子,但这是 PEP 380 定义的方式,当我们意识到这一点之后就说得通了:yield from 结构会在内部自动捕获 StopIteration 异常。这种处理方式与for 循环处理 StopIteration 异常的方式一样:循环机制使用用户易于理解的方式处理异常。对 yield from 结构来说,解释器不仅会捕获 StopIteration 异常,还会把 value 属性 的值变成 yield from 表达式的值。可惜,我们无法在控制台中使用交互的方式测试这种行为,因为在函数外部使用 yield from(以及 yield)会导致句法出错。
本文介绍了一个特殊的协程版本,该协程在最后返回一个累计值而不是产出中间结果。通过使用Python的namedtuple,协程能够返回一个包含计数和平均值的结构化结果。

948





