虫虫

【python】中的 yield from 句法

2019-07-04  本文已影响38人  Alcazar
yield from 句法

在生成器中使用yield from subgen()时,subgen会获得控制权,把产出的值传给生成器的调用方,即调用方可以直接控制subgen。与此同时,gen会阻塞,等待subgen终止。

【简单应用】用来简化for循环中的yield表达式

举个栗子:

def gen():
    for c in 'AB':
        yield c

    for i in range(1,3):
        yield i

print(list(gen()))

前后对比

def gen():
    yield from 'AB'
    yield from range(1,3)
    
print(list(gen()))

【解释】yield from x表达式对x对象所做的第一件事是,调用iter(x),从中获取迭代器。因此,x可以是任何可迭代的对象。


【应用升级】把职责委托给子生成器

yield from 的主要功能是打开双向通道,把最外层的调用方与最内层的子生成器连接起来,这样二者可以直接发送和产出值,还可以直接传入异常,而不用在位于中间的协程中添加大量处理异常的样板代码。通过这个结构,协程可以把功能委托给子生成器。

主要术语:

graph LR
调用方 ==> 委派生成器
委派生成器 ==> 子生成器

【店小二】:各位看官,下面的代码请查收@ @

from collections import namedtuple

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

# 子生成器
def averager():
    total = 0.0
    count = 0
    average = None

    while True:
        # main 方法中发送的各种值,会绑定到term变量上
        term = yield
        
        # 子生成器终止的条件
        if term is None:
            break

        total += term
        count += 1
        average = total / count
    
    # 返回值会成为grouper中 yield from表达式的值
    return Result(count,average)

# 委派生成器
def grouper(results,key):
    while True:
        # 每次迭代都会生成一个averager实例。每个生成器都是本协程(grouper)使用的生成器对象。
        results[key] = yield from averager()

# 客户端代码
def main(data):
    results = {}
    for key,values in data.items():
        # results 用来存储结果
        group = grouper(results, key)
        # 预激活协程
        next(group)
        for value in values:
            # 发送的每个值都会经由grouper的yield from处理,通过管道传给averager实例。同时,当前的grouper实例,会在yield from 处暂停。
            group.send(value)
        # 把None值传入grouper,导致当前的averager实例终止,并让grouper继续运行,再创建一个aveager实例,处理下一组值。
        group.send(None)
    print(results)

data = {
    'girls;kg':[40.9,38.5,44.3,42.2,45.2,41.7,44.5,38.0,40.6,44.5],
    'girls;m':[1.6,1.51,1.4,1.3,1.41,1.39,1.33,1.46,1.45,1.43],
    'boys;kg':[39.0,40.8,43.2,40.8,43.1,38.6,41.4,40.6,36.3],
    'boys;m':[1.38,1.5,1.32,1.25,1.37,1.48,1.25,1.49,1.46]
}


main(data)

Output:
{'girls;kg': Result(count=10, average=42.040000000000006), 'girls;m': Result(count=10, average=1.4279999999999997), 'boys;kg': Result(count=9, average=40.422222222222224), 'boys;m': Result(count=9, average=1.3888888888888888)}

【提示】委派生成器在yield from 表达式处暂停时,调用方可以直接把数据发给子生成器,子生成器再把产出的值发给调用方。子生成器返回之后,解释器会抛出StopIteration异常,并把返回值附加到异常对象上,此时委派生成器会恢复运行。


【重点】

yield from 句法的注意点
yield from 句法的意义

【写在前面】下面的几点知识...有点复杂,考虑的如果不够全面的话,可能写的程序会崩。

如果发送的值不是None,那么会调用子生成器的send()方法。如果调用的方法抛出StopIteration异常,那么委派生成器恢复运行。任何其他异常都会向上冒泡,传给委派生成器。

如果调用throw()方法时抛出StopIteration异常,委派生成器恢复运行。StopIteration之外的异常会向上冒泡,传给委派生成器。

上一篇下一篇

猜你喜欢

热点阅读