yield
对于python生成器中的yield来说,yield item会产出一个值,提供给next()的调用方,然后挂起生成器,直到再调用next()。
def func():
for i in range(10):
yield i
f = func()
next(f)
协程
在协程中,yield通常出现在表达式的右边(data = yield),可以返回一个值,也可以不返回(如果yield后面没有表达式,则返回None)。 生成器的调用方可以使用send()方法发送数据,发送的数据会成为yield表达式的值。因此,生成器可以作为协程使用。
协程是一个过程,这个过程与调用方协作,产出由调用方提供的值
>>> def func():
... for i in range(10):
... r = yield i #遇到yield会暂时挂起生成器,并返回i
... print(r)
...
>>> f = func()
>>> next(f) # 启动生成器才能向生成器发送数据
0
>>> f.send(10)
10
1
*** 在使用f.send()之前,一定要先预激协程 ***
为了简化协程的用法,可以使用一个预激装饰器
import functools
def wrapper(func):
@functools.wraps(func) # 使用wraps是为了保留func的函数名
def inner(*args, **kwargs):
gen = func(*args, **kwargs)
next(gen)
return gen
return inner
def func():
for i in range(10):
r = yield i
print(r)
终止协程和异常处理
如果协程内没有处理异常, 协程会终止,再次调用协程,会抛出StopIteration异常
>>> f = func()
>>> f.send(10)
100
1
>>> f.send('hello')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in func
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
>>> f.send(10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
让协程返回值
在协程中使用return返回的值会放在StopIteration异常中的value变量中,捕获这个异常,然后获取即可
yield from
使用yield from 会自动捕获StopIteration异常,还会把value变量的值变成yield from 表达式的值。
def func():
for i in range(10):
r = yield i
yield from rang(10) 等同于func()
yield from 将调用方法与内层的子生成器连接起来, 外层的调用方法可以向内层的生成器直接传递值,子生成器返回的值由yield from接收.
下面看一个例子
#!/usr/bin/env python3
from collections import namedtuple
Result = namedtuple('Result', 'count average')# 子生成器
# 这个例子和上边示例中的 averager 协程一样,只不过这里是作为字生成器使用
def averager():
total = 0.0
count = 0
average = None
while True: # main 函数发送数据到这里
term = yield
if term is None: # 终止条件
break
total += term
count += 1
average = total/count
return Result(count, average) # 返回的Result 会成为grouper函数中yield from表达式的值
# 委派生成器
def grouper(results, key):
# 这个循环每次都会新建一个averager 实例,每个实例都是作为协程使用的生成器对象
while True: # grouper 发送的每个值都会经由yield from 处理,通过管道传给averager 实例。grouper会在yield from表达式处暂停,等待averager实例处理客户端发来的值。averager实例运行完毕后,返回的值绑定到results[key] 上。while 循环会不断创建averager实例,处理更多的值。
results[key] = yield from averager()
# 调用方
def main(data):
results = {}
for key, values in data.items():
# group 是调用grouper函数得到的生成器对象,传给grouper 函数的第一个参数是results,用于收集结果;第二个是某个键
group = grouper(results, key)
next(group)
for value in values: # 把各个value传给grouper 传入的值最终到达averager函数中;
# grouper并不知道传入的是什么,同时grouper实例在yield from处暂停
group.send(value) # 把None传入groupper,传入的值最终到达averager函数中,导致当前实例终止。然后继续创建下一个实例。
# 如果没有group.send(None),那么averager子生成器永远不会终止,委派生成器也永远不会在此激活,也就不会为result[key]赋值
group.send(None)
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, 41, 42, 43, 44, 54],
'girls;m': [1.5, 1.6, 1.8, 1.5, 1.45, 1.6],
'boys;kg':[50, 51, 62, 53, 54, 54],
'boys;m': [1.6, 1.8, 1.8, 1.7, 1.55, 1.6],
}
if __name__ == '__main__':
main(data)
网友评论