流畅的Python - 第16章-协程
Contents
yield关键字
yield关键字主要有两个使用方式:
yield item:会产出一个值,提供给next(...)的调用方。也可以不产生值,需要yield后面不跟任何参数,返回值为Nonei = yield:从调用方接收数据。调用方通过.send(...)的方式将数据提供给协程。yield:既不接收也不发送数据,纯粹作为流程控制使用。
将yield作为协程使用时,通常也是将其作为流程控制的调度器使用。
协程
简单的例子开始说起。
| |
产生的结果如下:
| |
yield执行顺序是:先执行yield item,再执行i = yield。
所以,要想i = yield生效,需要先调用next(...),让生成器启动起来。这个过程称为预激。
预激过程也可以使用装饰器来完成,可以自己定义,也可以使用系统自带的from coroutil import coroutine。
另外,再最后一个i = yield接收完数据后会抛出StopIteration的异常,异常捕获后的值是原始函数的返回值,这个是正常情况。
协程有四种状态:
GEN_CREATED:等待开始执行状态。GEN_RUNNING:解释器正在执行状态。GEN_SUSPENDED:在yield表达式处挂起。GEN_CLOSE:执行结束。
使用inspect.getgeneratorstate(...)函数能够捕获对应的状态。
协程的终止和异常处理
停止协程的方式还是让它产生异常。
- 可以隐式的
.send(...)一个不符合要求的数据,使其产生异常退出,如果有对应的捕获异常的函数,则能够正常结束。 - 显式的调用
throw和close方法也能结束协程。throw方法是致使生成器在暂停的yield表达式处抛出指定的异常。close方法是致使生成器在暂停的yield表达式处抛出GeneratorExit异常。
在
for循环中,如果for的对象是一个生成器的话,到最后也会抛出异常,只不过循环机制已经将异常做了相应的处理。
yield from
yield from类似于其他语言的await关键字(还没研究过这个关键字…)。
用法一:链接可迭代的对象
yield from最简单的作用就是链接可迭代的对象。
| |
打印出的结果如下:
| |
用法二:委派生成器
yield from还有一个更重要的用法!打开双向通道,把最外层的调用方与最内层的自生成器连接起来,两者可以直接发送和产生值,还可以直接传入异常,而不用在位于中间的协程中添加大量处理异常的样板代码。
| |
在上述例子中,调用方main函数内部通过参数group定义为生成器,在委派生成器grouper()中使用yield from Averager(...)时,子生成器Averager()会获得控制权,把生成的值传给委派生成器grouper()的调用方main函数,即main()直接和Averager()接触。与此同时委派生成器grouper()将会阻塞,直到子生成器Averager()终止。

整个过程中,委派生成器只做中间管道的部分。