摘要:协程定义协程是指一个过程,这个过程与调用方协作,产出由调用方提供的值。当得到控制权时,会阻塞,同时等待终止。终止协程的方法该方法致使生成器在暂停的表达式处抛出异常。
协程
定义:协程是指一个过程,这个过程与调用方协作,产出由调用方提供的值。(协程中必定含有一条yield语句)
协程与生成器类似,都是定义体内包含yield关键字的函数。不过,在协程中,yield通常出现在表达式的右边(例如,data = yield),可以产出值,也可以不产出。
生成器不可以返回值,如果生成器中给return语句提供值,会抛出SyntaxError异常;
python新引入yield from 语句,可以把复杂的生成器重构成小型的嵌套生成器,省去了大量样板代码。
三个方法:. send() 方法,可以让调用方给协程发送数据,发送的数据会成为协程函数中 yield 表达式的值。
.throw() 方法,可以让调用方抛出异常
.close() 方法,可以让调用方终止协程
四个状态:"GEN_CREATED" 等待开始执行
"GEN_RUNNING" 解释器正在执行
"GEN_SUSPENDED" 在yield表达式处暂停
"GEN_CLOASED" 执行结束
协程只能处于这四个状态中的一个,当前状态可以由 inspect.getgeneratorstate(...)函数获取
因为send() 方法的参数会成为暂停的yield表达式的值,所以,仅当协程处于暂停状态时才能调用send()方法
协程需要被预激,预激是通过next()函数进行
给协程添加预激装饰器 functools.wraps(),可以省去协程的预激过程。
yield from在生成器gen中使用yield from subgen()时,subgen()会得到当前的控制权,把产出的值传给gen的调用方,即调用方可以直接跳过gen控制subgen。当subgen得到控制权时,gen会阻塞,同时等待subgen终止。
一个小例子:
def chain(*iters): for iter in iters: yield from iter lst_1 = "abc" lst_2 = "987" print(list(chain(lst_1, lst_2)))
运行结果:
["a", "b", "c", "9", "8", "7"]
这个例子还可以改写为:
def chain(): yield from "abc" yield from "987"
输出结果是一样的。
yield from 的主要功能是打开双向通道,把最外层的调用方与最内层的子生成器连接起来,这样,二者可以直接发送和产生值,甚至可以直接传入异常。
一个复杂的例子,计算中学生的平均身高和体重:
from collections import namedtuple Result = namedtuple("Result", "count average") # 子生成器 def averager(): # <1> total = 0.0 count = 0 average = None while True: term = yield # <2> if term is None: # <3> break total += term count += 1 average = total/count return Result(count, average) # <4> # 委派生成器 def grouper(results, key): # <5> while True: # <6> results[key] = yield from averager() # <7> # 客户端代码,即调用端 def main(data): # <8> results = {} for key, values in data.items(): group = grouper(results, key) # <9> next(group) # <10> for value in values: group.send(value) # <11> group.send(None) # important! <12> # print(results) # uncomment to debug 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)
运行结果:
9 boys averaging 40.42kg 9 boys averaging 1.39m 10 girls averaging 42.04kg 10 girls averaging 1.43m
委派生成器grouper()只是起到一个传输数据的作用,没有进行任何的数据处理。
生成器中都有一个无限循环 while True: 这个无限循环表明,只要调用方不断把值发送给这个协程,它就会一直接收值,然后生成结果。该循环结束条件:
调用方在协程上显式调用 .close() 方法,
或者没有对协程的引用,而被垃圾回收程序回收时,这个协程才会终止。
终止协程的方法generator.close()
该方法致使生成器在暂停的 yield 表达式处抛出 GeneratorExit 异常。
如果生成器处理了这个异常,生成器一定不能产生值,否则解释器会抛出RuntimeError异常。
如果生成器没有处理这个异常,或者抛出StopIteration异常,即生成器已经运行到最后,调用方也不会报错。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/42346.html
摘要:进程和线程究竟是什么东西传统网络服务模型是如何工作的协程和线程的关系和区别有哪些过程在什么时间发生在刚刚结束的上海站,来自七牛云存储的高级工程师许智翔带来了关于的分享中的进程线程协程同步异步回调。使用红黑树管理就绪队列。 进程和线程究竟是什么东西?传统网络服务模型是如何工作的?协程和线程的关系和区别有哪些?IO过程在什么时间发生? 在刚刚结束的 PyCon2014 上海站,来自七牛云...
摘要:并发的方式有多种,多线程,多进程,异步等。多线程和多进程之间的场景切换和通讯代价很高,不适合密集型的场景关于多线程和多进程的特点已经超出本文讨论的范畴,有兴趣的同学可以自行搜索深入理解。 编程中,我们经常会遇到并发这个概念,目的是让软件能充分利用硬件资源,提高性能。并发的方式有多种,多线程,多进程,异步IO等。多线程和多进程更多应用于CPU密集型的场景,比如科学计算的时间都耗费在CPU...
摘要:当前状态可以使用函数确定,该函数会返回下述字符串中的一个。解释器正在执行。打印消息,然后协程终止,导致生成器对象抛出异常。实例运行完毕后,返回的值绑定到上。 协程 协程可以身处四个状态中的一个。 当前状态可以使用inspect.getgeneratorstate(...) 函数确定,该函数会返回下述字符串中的一个。 GEN_CREATED 等待开始执行。 GEN_RUNNING 解...
阅读 1978·2021-11-23 10:03
阅读 4153·2021-11-22 09:34
阅读 2478·2021-10-08 10:05
阅读 2250·2019-08-30 15:53
阅读 1687·2019-08-30 13:56
阅读 1156·2019-08-29 16:52
阅读 1106·2019-08-26 13:31
阅读 3351·2019-08-26 11:45