生成器进化成协程
生成器是由迭代器进化而来,所以生成器对象有 iter 和 next 方法,可以使用 for 循环获得值,注意这里所说的 “获得值” 指的是下文代码块里 yield 语句中 yield 关键字后面的 i 。这是在 Python 2.5 时出现的特性,在 Python 3.3 中出现 yield from 语法之前,生成器没有太大用途。
但此时 yield 关键字还是实现了一些特性,且至关重要,就是生成器对象有 send 、throw 和 close 方法。这三个方法的作用分别是发送数据给生成器并赋值给 yield 语句、向生成器中抛入异常由生成器内部处理、终止生成器。这三个方法使得生成器进化成协程。
协程有四种存在状态:
GEN_CREATED 创建完成,等待执行
GEN_RUNNING 解释器正在执行(这个状态在下面的示例程序中无法看到)
GEN_SUSPENDED 在 yield 表达式处暂停
GEN_CLOSE 执行结束,生成器停止
可以使用 inspect.getgeneratorstate 方法查看协程的当前状态,举例如下:
inspect模块用于收集python对象的信息,可以获取类或函数的参数的信息,源码,解析堆栈,对对象进行类型检查等等
1 | import inspect |
代码说明如下:
1、创建生成器
2、查看生成器状态
3、这步操作叫做预激生成器(或协程),这是必须做的。在生成器创建完成后,需要将其第一次运行到 yield 语句处暂停
4、暂停状态的生成器可以使用 send 方法发送数据,此方法的参数就是 yield 表达式的值,也就是 yield 表达式等号前面的 value 变量的值变成 ‘Hello Shiyanlou’,继续向下执行完一次 while 循环,变量 i 被赋值,继续运行下一次循环,yield 表达式弹出变量 i
5、向生成器抛入异常,异常会被 try except 捕获,作进一步处理
6、close 方法终止生成器,异常不会被抛出
因为生成器的调用方也就是程序员自己可以控制生成器的启动、暂停、终止,而且可以向生成器内部传入数据,所以这种生成器又叫做协程,generator 函数既可以叫做生成器函数,也可以叫协程函数,这是生成器向协程的过渡阶段。
yield -> yield from
在 Python 3.3 中新增了 yield from 语法,如果将yield理解成“返回”,那么yield from就是“从什么(生成器)里面返回”,这是全新的语言结构,是 yield 的升级版。相比 yield ,该语法有两大优势,我们来举例说明它的用法。
区别示例
1 | def generator(): |
避免潜逃循环
yield:
1 | def chain(*args): |
yield from:
1 | def chain(*args): |
可以看到 yield from 语句可以替代 for 循环,避免了嵌套循环。同 yield 一样,yield from 语句也只能出现在函数体内部,有 yield from 语句的函数叫做协程函数或生成器函数。
yield from 后面接收一个可迭代对象,例如上面代码中的 iter_obj 变量,在协程中,可迭代对象往往是协程对象,这样就形成了嵌套协程。
转移控制权
1 | import time |
所谓 “转移控制权” 就是 yield from 语法可以将子生成器的控制权交给调用方 main 函数,在 main 函数内部创建父生成器 c ,控制 c.send 方法传值给子生成器。这是一个巨大的进步,在此基础上,Python 3.4 新增了创建协程的装饰器,这样非生成器函数的协程函数就正式出现了。