- yield两个释义:
产出:产出一个值,提供给 next(...) 调用方
让步:暂停执行生成器,让调用方继续工作 -
协程特点:
与生成器类似,定义体都是包含 yield 关键字的函数。
datum = yield,但是 yield 通常写在右边,此处产出 None
.send(datum) 是调用方提供数据给协程
next(...) 是协程前进执行至 yield 暂停,或者最后抛出 StopIteration
-
其中,next(coro) 是用于激活协程,效果与 coro.send(None) 一样。
- 对于 b = yield a,等到客户端代码再次激活协程时才会设定 b 的值。
- 移动平均值
>>> def average():
... count = 0
... average = None
... total = 0
... while True:
... term = yield average
... total += term
... count += 1
... average = total / count
...
>>> coro = average()
>>> next(coro)
>>> coro.send(10)
10.0
>>> coro.send(20)
15.0
>>> coro.send(30)
20.0
>>>
只有调用方在协程上调用 close() 方法,或没有协程引用而被垃圾回收时,这个协程才会停止。
预激协程的装饰器
from functools import wraps
def coroutine(func):
@wraps(func)
def primer(*args, **kwargs):
gen = func(*args, **kwargs)
next(gen)
return gen
return primer
装饰器也不过如此。
coroutine 是一个装饰器,最终返回 primer 函数,说明想用 primer 代替传入的 func。
所以,编写 primer 就是重新写 func 函数,但是可以调用之前的 func。
from coroutil import coroutine
@coroutine
def averager():
average = None
total = 0
count = 0
while True:
term = yield average
total += term
count += 1
average = total / count
>>> coro = averager()
>>> coro.send(10)
10.0
>>> coro.send(20)
15.0
>>> coro.close()
>>> from inspect import getgeneratorstate
>>> getgeneratorstate(coro)
'GEN_CLOSED'
>>>
让协程返回值
from collections import namedtuple
Result = namedtuple('Result', 'count average')
def averager():
count = 0
average = None
total = 0
while True:
term = yield
if term is None:
break # 一定要有 break,才会执行 return
total += term
count += 1
average = total / count
return Result(count, average)
>>> coro = averager()
>>> next(coro)
>>> coro.send(10)
>>> coro.send(20)
>>> coro.send(None)
Traceback (most recent call last):
File "", line 1, in
StopIteration: Result(count=2, average=15.0)
>>>
发送 None 终止循环,协程结束。
同时生成器对象抛出 StopIteration 异常,异常对象的 value 属性保存返回的值。
更妥当的做法:
>>> coro = averager()
>>> next(coro)
>>> coro.send(10)
>>> coro.send(20)
>>> try:
... coro.send(None)
... except StopIteration as exc:
... result = exc.value
...
>>> result
Result(count=2, average=15.0)
>>>
使用 yield from(await)
作用是代替只有 yield 表达式的 for 循环。
>>> def gen():
... for i in range(3):
... yield i
...
>>> def gen2():
... yield from range(3)
...
>>> list(gen())
[0, 1, 2]
>>> list(gen2())
[0, 1, 2]
>>>
链接可迭代对象(当然了,用 C 编写的 itertools.chain() 更好):
>>> def chain(*iterables):
... for i in iterables:
... yield from i # yield from 相当于又一次循环
...
>>> list(chain('abc', range(3), 'xyz'))
['a', 'b', 'c', 0, 1, 2, 'x', 'y', 'z']
>>>