yield from

1 yield from 

yield from关键字是在:PEP 380 -- Syntax for Delegating to a Subgenerator 中提出的

用于生成器将其部分操作委托给另外一个生成器,这允许将包含yield的一段代码分解出来并放在另外一个生成器中,此外,允许子生成器返回一个值,这个值可供委派生成器使用

from collections import namedtuple

Result = namedtuple("Result", "count average")

li = [40.9, 38.5, 44.3]


# 子生成器
def averager():
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield
        print("====", term)
        if term is None:
            break
        total += term
        count += 1
        average = total / count
    return Result(count, average)


# 委派生成器
def grouper(result, key):
    while True:
        result[key] = yield from averager()


# 调用方
def main():
    results = {}
    group = grouper(results, "kg")
    next(group)  # 调入 委派生成器, 然后调入子生成器,到yield
    for value in li:
        group.send(value)  # 直接传递到子生成器的yield
    group.send(None)

    # group.send(None)
    # print(results)


if __name__ == "__main__":
    main()

yield from的主要功能是打开双向通道,把最外层的调用方与最内层的子生成器连接起来,这样二者可以直接发送和产出值,还可以直接传入异常

yield from 的六个重要意义

关于yield from 六点重要的说明:

  1. 子生成器产出的值都直接传给委派生成器的调用方(即客户端代码)
  2. 使用send()方法发送给委派生成器的值都直接传给子生成器。如果发送的值为None,那么会给委派调用子生成器的__next__()方法。如果发送的值不是None,那么会调用子生成器的send方法,如果调用的方法抛出StopIteration异常,那么委派生成器恢复运行,任何其他异常都会向上冒泡,传给委派生成器
  3. 生成器退出时,生成器(或子生成器)中的return expr表达式会出发StopIteration(expr)异常抛出
  4. yield from表达式的值是子生成器终止时传给StopIteration异常的第一个参数。yield from 结构的另外两个特性与异常和终止有关。
  5. 传入委派生成器的异常,除了GeneratorExit之外都传给子生成器的throw()方法。如果调用throw()方法时抛出StopIteration异常,委派生成器恢复运行。StopIteration之外的异常会向上冒泡,传给委派生成器
  6. 如果把GeneratorExit异常传入委派生成器,或者在委派生成器上调用close()方法,那么在子生成器上调用clsoe()方法,如果它有的话。如果调用close()方法导致异常抛出,那么异常会向上冒泡,传给委派生成器,否则委派生成器抛出GeneratorExit异常

2 yield from与async

import sys
import time


def binary(n):
    if n <= 0:
        return 1
    l = yield from binary(n - 1)
    r = yield from binary(n - 1)
    return l + 1 + r


async def abinary(n):
    if n <= 0:
        return 1
    l = await abinary(n - 1)
    r = await abinary(n - 1)
    return l + 1 + r


def timeit(func, depth, repeat):
    t0 = time.time()
    for _ in range(repeat):
        o = func(depth)
        print(o)
        try:
            while True:
                o.send(None)
        except StopIteration:
            pass
    t1 = time.time()
    print('{}({}) * {}: total {:.3f}s'.format(
        func.__name__, depth, repeat, t1 - t0))


# timeit(binary, 19, 30)  # binary(19) * 30: total 7.388s
# timeit(abinary, 19, 30)  # abinary(19) * 30: total 7.357s   # 2着用时基本一致

 

posted @ 2019-06-25 11:49  Mr.GLH  阅读(627)  评论(0编辑  收藏  举报
Top↑