下面示例 是 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)
会导致句法出错。