协程

  • yield两个释义:
    产出:产出一个值,提供给 next(...) 调用方
    让步:暂停执行生成器,让调用方继续工作
  • 协程特点:
    与生成器类似,定义体都是包含 yield 关键字的函数。
    datum = yield,但是 yield 通常写在右边,此处产出 None
    .send(datum) 是调用方提供数据给协程
    next(...) 是协程前进执行至 yield 暂停,或者最后抛出 StopIteration


    协程_第1张图片
    协程
  • 其中,next(coro) 是用于激活协程,效果与 coro.send(None) 一样。


    协程_第2张图片
    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']
>>>

你可能感兴趣的:(协程)