对于大多数应用程序,
yield from只需按顺序从左可迭代产生所有内容:
def iterable1(): yield 1 yield 2def iterable2(): yield from iterable1() yield 3assert list(iterable2) == [1, 2, 3]
对于90%看到这篇文章的用户,我猜这对他们来说已经足够解释了。
yield from简单地 委托 给右边的迭代器。
协程
但是,还有一些更深奥的生成器环境在这里也很重要。关于生成器的一个鲜为人知的事实是它们可以用作协程。这不是超级常见的做法,但是您可以根据需要将数据发送到生成器:
def coroutine(): x = yield None yield 'You sent: %s' % xc = coroutine()next(c)print(c.send('Hello world'))旁:您可能想知道用例是什么(而且您并不孤单)。
contextlib.contextmanager装饰器就是一个例子。协同例程还可用于并行执行某些任务。我不知道有太多利用它的地方,但是google
app-engine的
ndb数据存储区API相当漂亮地将其用于异步操作。
现在,让我们假设您将
send数据发送到生成器,该生成器正在从另一个生成器产生数据…如何通知原始生成器?答案是它不在python2.x中,您需要自己包装生成器:
def python2_generator_wapper(): for item in some_wrapped_generator(): yield item
至少并非没有很多痛苦:
def python2_coroutine_wrapper(): """This doesn't work. Somebody smarter than me needs to fix it. . . Pain. Misery. Death lurks here :-(""" # See https://www.python.org/dev/peps/pep-0380/#formal-semantics for actual working implementation :-) g = some_wrapped_generator() for item in g: try: val = yield item except Exception as forward_exception: # What exceptions should I not catch again? g.throw(forward_exception) else: if val is not None: g.send(val) # Oops, we just consumed another cycle of g ... How do we handle that properly ...所有这些都变得微不足道
yield from:
def coroutine_wrapper(): yield from coroutine()
因为
yield from真正将( 一切! )委托给基础生成器。
返回语义
请注意,所讨论的PEP也会更改返回语义。尽管不是OP的直接问题,但是如果您愿意的话,值得快速离题。在python2.x中,您无法执行以下操作:
def iterable(): yield 'foo' return 'done'
这是一个
SyntaxError。随着的更新
yield,上述功能不合法。同样,主要用例是协同程序(请参见上文)。您可以将数据发送到生成器,并且它可以神奇地完成工作(也许使用线程?),而程序的其余部分则可以完成其他工作。当流控制传递回生成器时,
StopIteration将被提升(这对于生成器端来说是正常的),但是现在
StopIteration将具有数据有效负载。就像程序员改写了一样:
raise StopIteration('done')现在,调用者可以捕获该异常,并对数据有效载荷进行某些操作,以造福人类。



