个人技术分享

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)))