Python3.3新增的yield from表达式句法可把一个生成器的工作委托给一个子生成器。
在引入yield from之前,如果一个生成器根据另一个生成器生成的值产生值,则需要使用for循环。
def sub_yield():
yield 1.1
yield 1.2
yield 1.3
def main_yield():
yield 1
for s in sub_yield():
yield s
yield 2
yield 3
for i in main_yield():
print(i)
# 输出
# 1
# 1.1
# 1.2
# 1.3
# 2
# 3
使用yield from也能达到这样的效果,并且代码看起来更优雅!
def sub_yield():
yield 1.1
yield 1.2
yield 1.3
def main_yield():
yield 1
yield from sub_yield()
yield 2
yield 3
for i in main_yield():
print(i)
上面代码for循环是客户代码,main_yield是委托生成器,sub_yield是子生成器。yield from暂停main_yield,sub_yield接手,直到它耗尽。sub_yield产生的值会绕过main_yield,直接传给客户代码的for循环使用。在此期间,main_yield处于暂停状态,看不到绕过它的那些值,当sub_yield耗尽后,main_yield恢复执行。
子生成器中有return语句时,返回一个值,在委托生成器中,通过含有yield from的表达式可以捕获那个值。
def sub_yield():
yield 1.1
yield 1.2
yield 1.3
return 'Done'
def main_yield():
yield 1
result = yield from sub_yield()
print(f"{result}----->")
yield 2
yield 3
for i in main_yield():
print(i)
# 输出
# 1
# 1.1
# 1.2
# 1.3
# Done----->
# 2
# 3
了解yield from的基本作用后,可以使用下面示例说明它的实际用途。
重新实现Chain
itertools里面有个chain生成器。它的作用用于从多个可迭代对象中产出项,先迭代第一个可迭代对象,然后迭代第二个,直到最后一个可迭代对象。
import itertools
c = itertools.chain([1, 2, 3], 'abc', (4, 5))
print(list(c)) # [1, 2, 3, 'a', 'b', 'c', 4, 5]
yield版本
def chain(*iterables):
for it in iterables:
for i in it:
yield i
s = 'abc'
l = [1, 2, 3]
t = (4, 5)
print(list(chain(s, l, t)))
yield from版本
def chain(*iterables):
for it in iterables:
yield from it
s = 'abc'
l = [1, 2, 3]
t = (4, 5)
print(list(chain(s, l, t)))