协程和生成器的差别
- 作用
- 协程:调用方可以把值推送给协程
- 生成器:调用方会从生成器中拉取值
什么是协程
协程是值一个过程,这个过程与调用方协作,产出由调用方提供的值
将 yield 关键字用在表达式中,就可以算作是协程了。
协程的两种用法如下所示。
- 调用方推送值
- 不接收值时,控制流程:协程可以把控制器让步给中心调度程序,从而激活其他的协程
简单协程:将yield 关键字用在表达式中
创建协程对象后,调用next 函数,启动生成器,激活协程,在yield 处暂停
执行send 函数,会将传入的值赋值给赋值表达式的左边变量,并继续向下运行,直到遇到下一个yield 表达式 或者 终止。
终止协程 和 异常处理
异常处理的规则
- 在协程中,未处理的异常会向上冒泡。
generator.throw()
- 让生成器在暂停的yield 表达式处抛出指定的异常。
- 如果生成器处理了抛出的异常,代码会向前执行到下一个yield 表达式。
- 如果生成器没有处理抛出的异常,异常会向上冒泡。
generator.close()
- 让生成器在暂停的 yield 表达式处抛出 GeneratorExit 异常。
- 接受到这个异常后,调用方不会报错。
- 如果收到这个异常后,生成器一定不能产出值。
让协程在最后返回值
生成器函数定义体中的return 语句会触发生成器对象抛出StopIteration异常,并且表达式的值会偷偷传给调用方。
yield from 简单用法:替代产出值的嵌套for 循环
yield from 的经典用法:使用嵌套的生成器
yield from的主要功能是 打开双向通道,把最外层的调用方与最内层的子生成器连接起来,这样,调用方和子生成器可以直接发送和产出之,而不需要在委派生成器的位置添加大量处理异常的样板代码。
yield from的例子:读取男女学生的体重和身高,并计算出男女学生的体重平均值和身高平均值
输入数据如下
在子生成器中,计算 一组数据的平均值
在调用方的主函数中,遍历多组数据,在这个例子中是 男学生的体重列表、男学生的身高列表、女学生的体重列表、女学生的身高列表。
第一步:在调用方主函数中,执行到next 时,预激委派生成器grouper, 此时,进入while True循环,调用子生成器averager后,在yield from 处表达式暂停
第二步:内层for 循环调用group.send(value), 直接把值传给子生成器 averager. 同时,当前的grouper实例 在yield from 表达式处暂停。
在内层循环结束后,group实例依旧在yield from 表达式处暂停,因此,委派生成器中的 results[key] 赋值的语句还没有执行。
第三步:外层for 循环的 group.send(None), 终止子生成器,委派生成器 激活,为 results[key] 赋值。