Python中的协程

65 阅读3分钟

协程和生成器的差别

  • 作用
    • 协程:调用方可以把值推送给协程
    • 生成器:调用方会从生成器中拉取值

什么是协程

协程是值一个过程,这个过程与调用方协作,产出由调用方提供的值

将 yield 关键字用在表达式中,就可以算作是协程了。

协程的两种用法如下所示。

  • 调用方推送值
  • 不接收值时,控制流程:协程可以把控制器让步给中心调度程序,从而激活其他的协程

a776933e0fb9a94c2faf76db26db2a5.jpg

简单协程:将yield 关键字用在表达式中

创建协程对象后,调用next 函数,启动生成器,激活协程,在yield 处暂停

423ab02b3ea09a31ba4a4ec2fe74127.jpg

执行send 函数,会将传入的值赋值给赋值表达式的左边变量,并继续向下运行,直到遇到下一个yield 表达式 或者 终止。

96f0a773962573638d946f8c908da76.jpg

终止协程 和 异常处理

异常处理的规则

  • 在协程中,未处理的异常会向上冒泡。

generator.throw()

  • 让生成器在暂停的yield 表达式处抛出指定的异常。
  • 如果生成器处理了抛出的异常,代码会向前执行到下一个yield 表达式。
  • 如果生成器没有处理抛出的异常,异常会向上冒泡。

generator.close()

  • 让生成器在暂停的 yield 表达式处抛出 GeneratorExit 异常。
  • 接受到这个异常后,调用方不会报错。
  • 如果收到这个异常后,生成器一定不能产出值。

让协程在最后返回值

生成器函数定义体中的return 语句会触发生成器对象抛出StopIteration异常,并且表达式的值会偷偷传给调用方。

328f4d2f658c2856274e8e4e32c94e4.jpg

yield from 简单用法:替代产出值的嵌套for 循环

05b41f3ffbdc42aaa18fe1aa37910d4.jpg

yield from 的经典用法:使用嵌套的生成器

yield from的主要功能是 打开双向通道,把最外层的调用方与最内层的子生成器连接起来,这样,调用方和子生成器可以直接发送和产出之,而不需要在委派生成器的位置添加大量处理异常的样板代码。

3d8d913f07f273a9ff63bc5f003e8c2.jpg

yield from的例子:读取男女学生的体重和身高,并计算出男女学生的体重平均值和身高平均值

输入数据如下

934485a0ec2bf011f020474183989f0.jpg

在子生成器中,计算 一组数据的平均值

在调用方的主函数中,遍历多组数据,在这个例子中是 男学生的体重列表、男学生的身高列表、女学生的体重列表、女学生的身高列表。

第一步:在调用方主函数中,执行到next 时,预激委派生成器grouper, 此时,进入while True循环,调用子生成器averager后,在yield from 处表达式暂停 910dbe7e0bd1f99fb5b162d34235c17.jpg

第二步:内层for 循环调用group.send(value), 直接把值传给子生成器 averager. 同时,当前的grouper实例 在yield from 表达式处暂停。

在内层循环结束后,group实例依旧在yield from 表达式处暂停,因此,委派生成器中的 results[key] 赋值的语句还没有执行。 73180cbfdd52e577345ef3162f86f17.jpg

第三步:外层for 循环的 group.send(None), 终止子生成器,委派生成器 激活,为 results[key] 赋值。

c5efabcc77ecbe493865073e0aec218.jpg c5efabcc77ecbe493865073e0aec218.jpg