1、生成器与协程的关系
从语法上看,协程与生成器类似,都是定义体中包含yield关键字的函数。普通函数将return改为yield就变成生成器函数,生成器函数返回的结果就是生成器
但在协程中,yield通常出现在表达式的右边(如,x = yield),可以产出值,也可以不产出,如果yield关键字后面没有表达式,生成器就产出None
协程可能会从调用方接收数据,不过调用方把数据提供给协程使用的是.send(datum)方法,而不是next()函数,通常调用方会把值推送给协程
不管数据如何流动,yield都是一种流程控制工具,可以实现协作式多任务,协程可以把控制器让步给中心调度程序,从而激活其他的协程
#协程使用生成器函数定义:定义体中有yield关键字
#如下为一个简单的协程
def simple_coroutine():
print('start')
x = yield
print('x:',x)
print('finish')
my_coro = simple_coroutine()
next(my_coro)#第一次使用,首先需要调用next()方法启动生成器,如果生成器没有启动,没有yield语句处暂停,就无法发送数据
my_coro.send(6)
#生成器使用yield时,只用于返回生成的值,而不会接收值
#协程使用yield时,除了可以返回值,还可以接收值。生成器和协程在定义上很相近,在具体的实现上也是类似的
2、协程的状态
协程可以身处于4个状态中的一个,当前状态可以使用inspect.getgeneratorstate(…)函数确定,该函数会返回下述字符串中的一个。
'GEN_CREATED’等待开始执行
'GEN_RUNNING’解释器正在执行
'GEN_SUSPENDED’在yield处暂停
'GEN_CLOSED’执行结束
def simple_coro(a):
print('start:a = ',a)
#返回a的值,获取send()传入的值
b = yield a
print('received:b = ',b)
c = yield a + b
print('received:c = ',c)
#获得协程对象
my_coro = simple_coro(1)
from inspect import getgeneratorstate
print(getgeneratorstate(my_coro))
#向前执行协程到第一个yield表达式,打印start:a = 14消息后,产出a的值,并且暂停,等待为b赋值
next(my_coro)
print(getgeneratorstate(my_coro))
#把数字2发送给暂停的协程,计算yield表达式,得到2,把那个数绑定给b
#打印received:b = 2消息,产出a + b 的值3,然后协程暂停,等待为c赋值
print(my_coro.send(2))
#使用协程计算移动平均值,原始闭包实现
def make_avg():
count = 0
total = 0
def avg(new_value):
nonlocal count,total
count += 1
total += new_value
return total / count
return avg
avg = make_avg()
print(avg(5))
print(avg(6))
def make_avg():
total = 0
count = 0
avg = None
while True:#无限循环表明,只要调用方法不断把值发给这个协程,它就会一直接收,然后生成结果。
term = yield avg
total += term
count += 1
avg = total / count
avg_coro = make_avg()
next(avg_coro)
print(avg_coro.send(5))
print(avg_coro.send(6))
#调用next(coro_avg)函数后,协程会向前执行到yield表达式,产出average变量的初始值None,因此不会出现在控制台中
#此时,协程在yield表达式处暂停,等到调用方发送值,激活协程,把发送的值赋给term,更新total、count、average三个变量
#的值,开始while的下一次迭代,产出average变量的值,等待下一次为term变量赋值
#使用装饰器预激活协程
from functools import wraps
def corotine(func):
@wraps(func)
def wrapper(*args,**kwargs):
gen = func(*args,**kwargs)
next(gen)
return gen
return wrapper
@corotine
def make_avg():
total = 0
count = 0
avg = None
while True:#无限循环表明,只要调用方法不断把值发给这个协程,它就会一直接收,然后生成结果。
term = yield avg
total += term
count += 1
avg = total / count
avg_coro = make_avg()
print(avg_coro.send(5))
print(avg_coro.send(6))