【python】生成器中 yield from 句法

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

主要术语:

  • 委派生成器:
    包含 yield from <iterable> 表达式的生成器函数。

  • 子生成器:
    从 yield from 表达式中 <iterable>部分获取的生成器函数。

  • 调用方
    调用委派生成器的客户端(调用方)代码。

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处永远暂停。

  • 因为委派生成器相当于管道,所以可以把任意数量个委派生成器连接在一起:一个委派生成器使用yield from调用一个子生成器,而那个子生成器本身也是委派生成器,使用yield from调用另一个子生成器,以此类推。最终,这个链条要以一个只使用yield表达式的简单生成器结束;不过,也能以任何可迭代的对象结束。

  • 任何yield from链条都必须由客户驱动,在最外层委派生成器上调用next(...)函数或.send(...)方法。

yield from 句法的意义

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

  • 子生成器产出的值都直接传给委派生成器的调用方(即客户端代码)。

  • 使用send()方法发给委派生成器的值都直接传给子生成器。如果发送的值是None,那么会调用子生成器的next()方法。

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

  • 生成器退出时,生成器(或子生成器)中的return expr表达式会触发StopIteration(expr)异常抛出。

  • yield from表达式的值是子生成器终止时传给StopIteration异常的第一个参数。

  • 传入委派生成器的异常,除了GeneratorExit之外都传给子生成器的throw()方法。

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

  • 如果把GeneratorExit异常传入委派生成器,或者在委派生成器上调用close()方法,那么在子生成器上调用close()方法,如果它有的话。如果调用close()方法导致异常抛出,那么异常会向上冒泡,传给委派生成器;否则,委派生成器抛出GeneratorExit异常。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容