协程
协程可以身处四个状态中的一个。
当前状态可以使用inspect.getgeneratorstate(...) 函数确定,该函数会返回下述字符串中的一个。
'GEN_CREATED'
等待开始执行。
'GEN_RUNNING'
解释器正在执行。
'GEN_SUSPENDED'
在 yield 表达式处暂停。
'GEN_CLOSED'
执行结束。
def simple_coro2(a):
print('-> Started: a =', a)
b = yield a #等着赋值b 把a甩出去
print('-> Received: b =', b)
c = yield a + b
print('-> Received: c =', c)
my_coro_2 = simple_coro2(14)
from inspect import getgeneratorstate
print(getgeneratorstate(my_coro_2))
print(next(my_coro_2))
getgeneratorstate(my_coro_2)
print(my_coro_2.send(28))
# 没有yield 出来 所以没有返回值
print(my_coro_2.send(99))
getgeneratorstate(my_coro_2)
getgeneratorstate 函数指明,处于 GEN_SUSPENDED 状态(即协程在 yield 表达式处暂停)。
❺ 把数字 99 发给暂停的协程;计算 yield 表达式,得到 99,然后把那个数绑定给 c。
打印 -> Received: c = 99 消息,然后协程终止,导致生成器对象抛出
StopIteration 异常。
另一个案例
def averager():
total = 0.0
count = 0
average = None
while True:
term = yield average
total += term
count += 1
average = total / count
coro_avg = averager()
print(next(coro_avg))
print(coro_avg.send(10))
print(coro_avg.send(15))
print(coro_avg.send(20))
调用 next 函数,预激协程。
➊ 这个无限循环表明,只要调用方不断把值发给这个协程,它就会一直接收值,然后生
成结果。仅当调用方在协程上调用 .close() 方法,或者没有对协程的引用而被垃圾回收
程序回收时,这个协程才会终止。
➋ 这里的 yield 表达式用于暂停执行协程,把结果发给调用方;还用于接收调用方后面
发给协程的值,恢复无限循环。
终止协程和异常处理
发送某个哨符值,让协程退出。
内置的 None 和Ellipsis 等常量经常用作哨符值。
Ellipsis 的优点是,数据流中不太常有这个值。
throw
generator.throw(exc_type[, exc_value[, traceback]])
致使生成器在暂停的 yield 表达式处抛出指定的异常。
如果生成器处理了抛出的异常,代码会向前执行到下一个 yield 表达式,而产出的值会成为调用 generator.throw方法得到的返回值。
generator.close()
致使生成器在暂停的 yield 表达式处抛出 GeneratorExit 异常。
如果生成器没有处理这个异常,或者抛出了 StopIteration 异常(通常是指运行到结尾),调用方不会报错。
如果收到 GeneratorExit 异常,生成器一定不能产出值,否则解释器会抛出
RuntimeError 异常。
两种停止方式
exc_coro.throw(ZeroDivisionError)
exc_coro.close()
让协程返回值
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:
break
total += term
count += 1
average = total / count
return Result(count, average)
coro_avg = averager()
next(coro_avg)
coro_avg.send(30)
coro_avg.send(6.5)
try:
coro_avg.send(None)
except StopIteration as exc:
result = exc.value
print(result)
捕获 StopIteration 异常,获取 averager 返回的值
yield from 结构会在内部自动捕获 StopIteration 异常。
这种处理方式与 for 循环处理 StopIteration 异常的方式一样:循环机制使用用户易于理解的方式处理异常。
对 yield from 结构来说,解释器不仅会捕获 StopIteration 异常,还会把value 属性的值变成 yield from 表达式的值。
使用yield from
yield from 结构唯一的作用是替代产出值的嵌套 for 循环, 这句话不对
yield from 的主要功能是打开双向通道,把最外层的调用方与最内层的子生成器连接起来,
这样二者可以直接发送和产出值,还可以直接传入异常,
而不用在位于中间的协程中添加大量处理异常的样板代码。
有了这个结构,协程可以通过以前不可能的方式委托职责。
案例
委派生成器
包含 yield from <iterable> 表达式的生成器函数。
子生成器
从 yield from 表达式中 <iterable> 部分获取的生成器。这就是 PEP 380 的标题
(“Syntax for Delegating to a Subgenerator”)中所说的“子生成器”(subgenerator)。
调用方
PEP 380 使用“调用方”这个术语指代调用委派生成器的客户端代码。在不同的语境
中,我会使用“客户端”代替“调用方”,以此与委派生成器(也是调用方,因为它调用了子
生成器)区分开。
不使用yield from
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: # ➌
break
total += term
count += 1
average = total / count
return Result(count, average) # ➍
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],
}
def main(data):
result = {}
for key, values in data.items():
coro_avg = averager()
next(coro_avg)
for value in values:
coro_avg.send(value)
try:
coro_avg.send(None)
except StopIteration as exc:
result[key] = exc.value
print(result)
if __name__ == '__main__':
main(data)
- 这里的try: catch stop异常要一直存在
用yiled from 及委派生成器作用
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: # ➌
break
total += term
count += 1
average = total / count
return Result(count, average) # ➍
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],
}
def grouper(results):
while True:
res_obj = yield from averager()
results.append(res_obj)
def main(data):
results = []
for key, values in data.items():
coro_avg = grouper(results)
next(coro_avg)
for value in values:
coro_avg.send(value)
# 这个None是停止返回 哨兵
coro_avg.send(None)
print(results)
# report(result)
# 输出报告
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))
if __name__ == '__main__':
main(data)
官方的案例
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: # ➌
break
total += term
count += 1
average = total / count
return Result(count, average) # ➍
# 委派生成器
def grouper(results, key): # ➎
while True: # ➏
results[key] = yield from averager() # ➐
# 客户端代码,即调用方
def main(data): # ➑
results = {}
for key, values in data.items():
group = grouper(results, key) # ➒
next(group) # ➓
for value in values:
group.send(value) # ⓫
group.send(None) # 重要! #⓬
print(results) # 如果要调试,去掉注释
# report(results)
# 输出报告
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)
❸ 至关重要的终止条件。如果不这么做,使用 yield from 调用这个协程的生成器会永
远阻塞。
❹ 返回的 Result 会成为 grouper 函数中 yield from 表达式的值
❻ 这个循环每次迭代时会新建一个 averager 实例;每个实例都是作为协程使用的生成
器对象。
❼ grouper 发送的每个值都会经由 yield from 处理,通过管道传给 averager 实
例。grouper 会在 yield from 表达式处暂停,等待 averager 实例处理客户端发来的
值。averager 实例运行完毕后,返回的值绑定到 results[key] 上。while 循环会不断
创建 averager 实例,处理更多的值。
yield from 结构会在内部自动捕获 StopIteration 异常。这种处理方
式与 for 循环处理 StopIteration 异常的方式一样:循环机制使用用户易于理解的方式
处理异常。对 yield from 结构来说,解释器不仅会捕获 StopIteration 异常,还会把
value 属性的值变成 yield from 表达式的值。
总结
这里先不总结 带我把协程 程序加进去
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。