鱼C论坛

 找回密码
 立即注册
查看: 2308|回复: 2

[技术交流] Python 小技巧 075:Python 的 yield from

[复制链接]
发表于 2020-4-11 18:48:18 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 zltzlt 于 2020-4-11 18:48 编辑

Python yield from


yield from 是 Python 3.3 新增的语法,这篇帖子将会介绍他的基本用法和高级用法。

一、yield from 的简单用法

它可以替代内层的 for 循环。如果一个生成器需要生成另一个生成器生成的值,有两种方法:

1. 使用 for 循环

    例如:

  
>>> def gen():
        for i in range(5):
                yield i

                
>>> list(gen())
[0, 1, 2, 3, 4]

2. 直接使用 yield from

    例如:

  
>>> def gen():
        yield from range(5)

        
>>> list(gen())
[0, 1, 2, 3, 4]

yield from 的原理是,先获取需要 “yield from” 的迭代器,然后遍历这个迭代器,yield 它的每一个值,直到迭代器抛出 StopIteration。

利用 yield from 可以实现 itertools 的 chain 生成器:
>>> def chain(*iterables):
        for i in iterables:
                yield from i


>>> list(chain(range(3), range(25, 30), range(90, 100, 2)))
[0, 1, 2, 25, 26, 27, 28, 29, 90, 92, 94, 96, 98]

二、yield from 的高级用法

当然,虽然 yield from 是 yield 的改进版,但是如果仅有上面一点点作用,Python 之父大可不必将它添加进 Python 语法里。

yield from 还改进了 yield 的一些缺点,例如 yield 无法获取生成器 return 的返回值。

我们都知道,在遍历一个生成器并 yield 它的每一个元素时,如果使用 for 语句去迭代生成器,则不会显式地触发 StopIteration 异常,而是自动捕获 StopIteration 异常。

所以如果遇到 return,只会终止迭代,而不会触发异常,因此也就没办法获取 return 的值。

例如:
def gen():
    for i in [3, 5, 7, 9, 8, 3, 6]:
        if i % 2 == 0:    # 遇到第一个偶数强制退出
            return '我被迫退出了'
        yield i


for i in gen():
    print(i)

打印的是 3 5 7 9,迭代到 8 的时候生成器 return 时隐式地触发了 StopIteration 异常,但是 for 自动捕捉了这个异常并且退出了循环。

但是使用 next(generator) 一次一次地迭代就会触发 StopIteration 异常,这时我们就可以捕捉并打印生成器的返回值了:
def gen():
    for i in [3, 5, 7, 9, 8, 3, 6]:
        if i % 2 == 0:    # 遇到第一个偶数强制退出
            return '我被迫退出了'
        yield i


g = gen()
while True:
    try:
        print(next(g))
    except StopIteration as e:
        print("value:", e.value)
        break

执行效果:
3
5
7
9
value: 我被迫退出了

现在我们用 yield from 实现同样的功能:
def gen():
    for i in [3, 5, 7, 9, 8, 3, 6]:
        if i % 2 == 0:  # 遇到第一个偶数强制退出
            return '我被迫退出了'
        yield i


def wrap_gen(generator):  # 定义一个包装生成器的生成器,它的本质还是生成器
    result = yield from generator  # 自动触发 StopIteration 异常,并且将 return 的返回值赋值给 yield from 表达式的结果,即 result
    print("result:", result)


def main():
    for j in wrap_gen(gen()):
        print(j)


if __name__ == '__main__':
    main()

执行结果:
3
5
7
9
result: 我被迫退出了

可以看出 yield from 具有以下的特点:

yield from iterable 结构会在内部自动捕获生成器的 StopIteration 异常。

这种处理方式与 for 循环处理 StopIteration 异常的方式一样。

而且对 yield from 结构来说,解释器不仅会捕获 StopIteration 异常,还会把 return 返回的值或者是 StopIteration 的 value 属性的值变成 yield from 表达式的值,即上面的 result。

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-4-27 13:20:34 | 显示全部楼层
学习了!
话说下一次更新是什么时候???
@zltzlt
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-8-1 15:08:23 | 显示全部楼层
催更
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-25 14:28

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表