likes
comments
collection
share

python中的yield from有那么难懂么?一.什么是yield from yield from 是一个在Pyth

作者站长头像
站长
· 阅读数 30

一.什么是yield from

yield from 是一个在Python中用于简化协程中生成器委托的语法结构。它允许一个协程将一部分工作委托给另一个生成器,然后等待生成器完成并收集其结果,从而减少了协程中的代码复杂性。

python中的yield from有那么难懂么?一.什么是yield from yield from 是一个在Pyth

二.yield from的简单实现

yield是每次惰性返回一个值,其实从名字中就能看出,yield fromyield的升级改进版本,如果将yield理解成“返回”,那么yield from就是“从什么(生成器)里面返回”,这就构成了yield from的一般语法,即:

# yield from 的用法很简单 EXPR是一个可迭代对象 generator
result = yield from EXPR

demo:

def sub_generator():
    yield 1
    yield 2
    yield 3


def main_generator():
    yield 'A'
    yield from sub_generator()  # 委托给子生成器
    yield from [11, 12, 13]
    yield from (21, 22, 23)
    yield from range(31, 34)
    yield 'B'


for item in main_generator():
    print(item)

总结:

生成器 、元组、 列表、range()函数产生的序列等可迭代对象”返回另外一个生成器。

yield只是返回一个元素。从这个层面来说,有下面的等价关系:yield from iterable本质上等于for item in iterable: yield item

三.yield from的高级应用

1.针对yiled无法获取生成器return的返回值

在使用yield生成器的时候,如果使用for语句去迭代生成器,则不会显式的出发StopIteration异常,而是自动捕获StopIteration异常,所以如果遇到return,只是会终止迭代,而不会触发异常,故而也就没办法获取return的值。如下:

def test_generator():
    for i in range(5):
        if i == 2:
            return "我被迫中断了"
        yield i


def main(g):
    try:
        for i in g:  # 不会显式触发异常,故而无法获取到return的值
            print(i)
    except StopIteration as e:
        print(e.value)


g = test_generator()
main(g)

从上面的例子可以看出,for 迭代语句不会显式触发异常,故而无法获取到return的值,迭代到2的时候遇到return语句,隐式的触发了StopIteration异常,就终止迭代了,但是在程序中不会显示出来。

def test_generator():
    for i in range(5):
        if i == 2:
            return "我被迫中断了"
        yield i

        
def main2(g):
    try:
        print(next(g))  # 每次迭代一个值,则会显式出发StopIteration
        print(next(g))
        print(next(g))
        print(next(g))
        print(next(g))
    except StopIteration as e:
        print(e.value)  # 获取返回的值

        
g = test_generator()
main2(g)

现在我们使用yield from来完成上面的同样的功能:

def test_generator():
    for i in range(5):
        if i == 2:
            return "我被迫中断了"
        yield i


def wrap_test_generator(g):
    result = yield from g
    print(result)


def main(g):
    for j in g:
        print(j)


g = test_generator()
g = wrap_test_generator(g)
main(g)

从上面的比较可以看出,yield from具有以下几个特点:

“调用方——>生成器函数(协程函数)”;

“调用方——>生成器包装函数——>生成器函数(协程函数)”;

return返回的值或者是StopIterationvalue属性的值变成 yield from表达式的值,即上面的result

2.yield from实现的数据传输通道

yield涉及到“调用方与生成器两者”的交互,生成器通过next()的调用将值返回给调用者,而调用者通过send()方法向生成器发送数据;

委派生成器:包含yield from 表达式的生成器函数;即上面的wrap_my_generator生成器函数

子生成器:从yield from表达式中 部分获取的生成器;即上面的test_generator生成器函数

调用方:调用委派生成器的客户端代码;即上面的main生成器函数

委派生成器在yield from表达式处暂停时,调用方可以直接把数据发给子生成器,子生成器再把产出的值发给调用方。子生成器返回之后,解释器会抛出StopIteration异常,并把返回值附加到异常对象上,此时委派生成器会恢复。

总结:

yield from主要设计用来向子生成器委派操作任务,但yield from可以向任意的可迭代对象委派操作;

委派生成器(group)相当于管道,所以可以把任意数量的委派生成器连接在一起---一个委派生成器使用yield from调用一个子生成器,而那个子生成器本身也是委派生成器,使用yield from调用另一个生成器。

def sub_generator():
    received = yield 'sub_generator says hello'
    yield f'sub_generator received: {received}'


def main_generator():
    response = yield from sub_generator()
    yield f'main_generator received: {response}'


gen = main_generator()
print(next(gen))  # 输出 'sub_generator says hello'
print(gen.send('Hi from main_generator'))  # 输出 'sub_generator received: Hi from main_generator'

yield from可以建立双向通信,使调用生成器和委托生成器可以相互传递值。

上述示例中,main_generator 发送消息给 sub_generator,然后 sub_generator 将回应传回给 main_generator

转载自:https://juejin.cn/post/7408037561236504587
评论
请登录