`yield from foo()` 和 `for x in foo(): yield x` 的区别

12
在 Python 中,大多数使用 yield from 的示例都解释为“yield from”可以理解为
yield from foo()

类似于

for x in foo(): yield x

另一方面,它似乎并不完全相同,其中还添加了一些魔法。我对于使用一个带有我不理解的魔法的函数感到有点不安。我需要了解哪些关于yield from的魔法知识,以避免陷入魔法产生的意外情况?魔法提供了哪些优势,我应该注意什么?


4
请参阅PEP-380 - jonrsharpe
1
相关问题:https://dev59.com/zIDba4cB1Zd3GeqPClCx - Bakuriu
1个回答

13
foo()返回一个常规的可迭代对象时,这两种方式是等价的。当foo()也是一个生成器时,“魔法”就发生了。此时,yield from foo()for x in foo(): yield x的情况有很大的不同。
使用generator.send()方法,可以将数据发送到生成器中。当您使用for循环时,yield x表达式会“接收”已发送的数据;foo()生成器永远不会看到这个数据。但是,当您使用yield from时,已发送的数据直接传递给被委托的生成器当前暂停的任何yield表达式。换句话说,yield from将已发送的数据传递给被委托的生成器,以便它可以接收到该数据。
你还可以使用generator.throw()在生成器中引发异常; 在使用for循环的情况下,异常是从yield x行引发的,而使用yield from时,异常会再次传递; 异常是在foo()内部引发的。
这意味着在委托迭代期间,yield from实质上替换了当前生成器。
被委托的生成器也可以与父生成器通信,完成操作时,引发的StopIteration异常的.value属性将作为yield from表达式的值返回。您可以在被委托的foo()生成器中使用return <expression>设置该异常的值,或者可以明确使用raise StopIteration(<expression>)

yield from 是通过PEP 380:委派生成器语法引入到语言中的。


1
+1 简单易懂的解释。yield from 允许你将一个生成器重构为多个函数,而不改变代码的行为(尽管嵌套的生成器仍然没有被优化,据我所知,例如实现幂集时可能出现 O(n*k) 的行为,而不是 O(n),其中 n=2**k)。 - jfs
这确实为你的答案奠定了基础:“当foo()返回一个常规可迭代对象时,两者是等价的。当foo()也是一个生成器时,“魔法”就发挥作用了。”想要委托给另一个生成器?使用yield from - sargas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接