1 预激装饰器 2 3 讨论如何终止协程之前,我们要先谈谈如何启动协程。使用协程之前必须预激,可是这一 4 步容易忘记。为了避免忘记,可以在协程上使用一个特殊的装饰器。接下来介绍这样一个 5 装饰器。 6 7 预激协程的装饰器, 8 from functools import wraps 9 10 def corountine(func): 11 ''' 12 装饰器:向前执行到第一个 yield 表达式,预激协程 func 13 :param func: 14 :return: 15 ''' 16 @wraps(func) # functools.wraps 则可以将原函数对象的指定属性复制给包装函数对象, 默认有 __module__、__name__、__doc__,或者通过参数选择 17 def primer(*args, **kwargs): # 把被装饰的生成器函数替换成这里的 primer 函数;调用 primer 函数时,返回预激后的生成器 18 gen = func(*args, **kwargs) # 调用被装饰的函数,获取生成器对象。 19 next(gen) # 预激生成器 20 return gen # 返回生成器 21 return primer 22 23 24 @corountine # 预激装饰器 25 def coro_average(): 26 total = 0.0 27 count = 0 28 average = None 29 while 1: 30 term = yield average 31 total += term 32 count += 1 33 average = total/count 34 35 coro3 = coro_average() 36 #print (coro3.send(None)) # 若没有 预激装饰器 需要 调用 send(None) 或 next(coro) 完成预激,即让代码跑到第一个 yield 处 37 print (coro3.send(5)) 38 print (coro3.send(7)) 39 print (coro3.send(10)) 40 coro3.close() 41 42 这个无限循环表明,只要调用方不断把值发给这个协程,它就会一直接收值,然后生 43 成结果。仅当调用方在协程上调用 .close() 方法,或者没有对协程的引用而被垃圾回收 44 程序回收时,这个协程才会终止。 45 这里的 yield 表达式用于暂停执行协程,把结果发给调用方;还用于接收调用方后面 46 发给协程的值,恢复无限循环。 47 使用协程的好处是, total 和 count 声明为局部变量即可,无需使用实例属性或闭包在 48 多次调用之间保持上下文。 49 50 调用 next(coro3) 函数后,协程会向前执行到 51 yield 表达式,产出 average 变量的初始值——None,因此不会出现在控制台中。此 52 时,协程在 yield 表达式处暂停,等到调用方发送值。 coro3.send(5) 那一行发送 53 一个值,激活协程,把发送的值赋给 term,并更新 total、 count 和 average 三个变量 54 的值,然后开始 while 循环的下一次迭代,产出 average 变量的值,等待下一次为 55 term 变量赋值.