Python 小技巧 075:Python 的 yield from
本帖最后由 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())
2. 直接使用 yield from
例如:
>>> def gen():
yield from range(5)
>>> list(gen())
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)))
二、yield from 的高级用法
当然,虽然 yield from 是 yield 的改进版,但是如果仅有上面一点点作用,Python 之父大可不必将它添加进 Python 语法里。
yield from 还改进了 yield 的一些缺点,例如 yield 无法获取生成器 return 的返回值。
我们都知道,在遍历一个生成器并 yield 它的每一个元素时,如果使用 for 语句去迭代生成器,则不会显式地触发 StopIteration 异常,而是自动捕获 StopIteration 异常。
所以如果遇到 return,只会终止迭代,而不会触发异常,因此也就没办法获取 return 的值。
例如:
def gen():
for i in :
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 :
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 :
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。 学习了!
话说下一次更新是什么时候???{:10_277:}
@zltzlt 催更
页:
[1]