Python 中的 yield from 关键字

Python 3.3 新增 yield from 语法,是理解协程的基础

#0 了解一下 itertools.chain

itertools.chain() 方法可以将不同的迭代类型连接起来进行 for 循环遍历。

from itertools import chain
lst = [1,2,3]
dic = {
    "tom":"www.tom.com",
    "bob":"bob.com"
}
# 使用 chain 方法可以直接对三个进行 for 循环
for value in chain(lst,dic,range(5,10)):
     print(value)

>>>
1
2
3
tom
bob
5
6
7
8
9

那么 itertools.chain() 是如何实现的呢?

# chain 的实现方法
# *args
# **kwargs 
def chainer(*args, **kwargs):
    for iterable in args:
        for value in iterable:
            yield value

for value in chainer(lst,dic,range(5,10)):
    print(value)

>>>
1
2
3
tom
bob
5
6
7
8
9

有了 yield from之后,我们还可以进一步少些代码:

# chain 的实现方法
# *args
# **kwargs 
def chainer(*args, **kwargs):
    for iterable in args:
        yield from iterable

for value in chainer(lst,dic,range(5,10)):
    print(value)

>>>
1
2
3
tom
bob
5
6
7
8
9

看,效果其实一模一样!应该可以大致感觉到yield from的特性了吧!yield from 后面操作一个 iterable 类型的对象可以直接获取到 iterable 类型的对象的值并返回。而 yield则不一样!

#1 yield fromyield 的区别

def g1(iterable):
    yield range(10)

for v in g1(range(10)):
    print(v)

>>>
range(0, 10)
def g2(iterable):
    yield from range(10)

for v in g2(range(10)):
    print(v)

>>>
0
1
2
3
4
5
6
7
8
9

#2 yield from 高级特性及 Coroutine 实现

1.main 调用方,func_yield_from 委托生成器, iterable 子生成器
2.yield from 会在调用方和子生成器直接建立一个通道,双向通道,可以互相通

def func_yield_from(iterable):
    yield from iterable


def main():
    g = func_yield_from(range(10))
    g.send(None)

看一个具体的实例:

final_result = {}

# 子生成器
def sum_sales(product_name):
    total = 0
    nums = []
    while True:
        x = yield

        if x:
            print(product_name + "销量:", x)

        # 当 send None 的时候,跳出 while 循环
        if not x:
            break
        
        total += x
        nums.append(x)

    return total, nums


# 委托生成器
def middle(key):
    while True:
        final_result[key] = yield from sum_sales(key)
        print("Complete!",final_result[key])


# 调用方
def main():
    dataset = {
        'apple': [1200, 1330, 500],
        'orange': [400, 600, 1000],
        'banana': [100, 500, 50]
    }

    for key,dataset in dataset.items():
        
        print('key:', key)
        print('dataset:',dataset)
        
        m = middle(key)
        
        # 预激 middle 协程
        m.send(None)

        # 给协程传递每一组的值
        for value in dataset:
            m.send(value)
        
        # 传递 None 终止 while 循环
        m.send(None)
    
    print(final_result)

if __name__ == "__main__":
    main()

输出如下:

>>>
key: apple
dataset: [1200, 1330, 500]
apple销量: 1200
apple销量: 1330
apple销量: 500
Complete! (3030, [1200, 1330, 500])
key: orange
dataset: [400, 600, 1000]
orange销量: 400
orange销量: 600
orange销量: 1000
Complete! (2000, [400, 600, 1000])
key: banana
dataset: [100, 500, 50]
banana销量: 100
banana销量: 500
banana销量: 50
Complete! (650, [100, 500, 50])
{'apple': (3030, [1200, 1330, 500]), 'orange': (2000, [400, 600, 1000]), 'banana': (650, [100, 500, 50])}

#3 yield from 背后做的事情

  1. try-catch 异常
# 没有 yield 的情况
def sum_sales(product_name):
    total = 0
    nums = []
    while True:
        x = yield

        if x:
            print(product_name + "销量:", x)

        # 当 send None 的时候,跳出 while 循环
        if not x:
            break
        
        total += x
        nums.append(x)

    return total, nums

if __name__ == "__main__":
    gen = sum_sales('apple')
    gen.send(None)
    gen.send(1200)
    gen.send(1000)
    gen.send(800)
   # gen.send(None)
>>>
apple销量: 1200
apple销量: 1000
apple销量: 800

去掉 gen.send(None)注释之后:

>>>
apple销量: 1200
apple销量: 1000
apple销量: 800
Traceback (most recent call last):
  File "/4_yield_from_try_catch_except.py", line 27, in <module>
    gen.send(None)
StopIteration: (3000, [1200, 1000, 800])

可见抛出了一个异常,并返回了我们需要的值。所以,在没有 yield from 的情况下,我们需要捕获这个异常:

if __name__ == "__main__":
    gen = sum_sales('apple')
    gen.send(None)
    gen.send(1200)
    gen.send(1000)
    gen.send(800)
    try:
        gen.send(None)
    except StopIteration as e:
        result = e.value
        print(result)

这样,就能正常了,不仅不会抛出异常,还能够获取到我们需要的值了:

apple销量: 1200
apple销量: 1000
apple销量: 800
(3000, [1200, 1000, 800])

所以说,单纯不用yield from的话,我们就需要写异常处理之类的逻辑,进一步减少代码量。

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

推荐阅读更多精彩内容

  • PYTHON-进阶-ITERTOOLS模块小结转自wklken:http://wklken.me/posts/20...
    C_Y_阅读 947评论 0 2
  • 本文翻译自Functional Programming Howto 本文将介绍Python中函数式编程的特性。在对...
    大蟒传奇阅读 2,589评论 4 14
  • 内置函数Python解释器内置了许多功能和类型,总是可用的。他们是按字母顺序列在这里。 abs(x)返回一个数的绝...
    uangianlap阅读 1,205评论 0 0
  • 你不知道JS:异步 第四章:生成器(Generators) 在第二章,我们明确了采用回调表示异步流的两个关键缺点:...
    purple_force阅读 941评论 0 2
  • 记忆里还停留着窗外打着绿竹的雨 和映着你们侧脸的窗户 时间一遍遍刷新而变得模糊 因为再见俩个字变得很无辜 青春变成...
    洛之亦阅读 106评论 0 2