python 3.3.2+中的yield from和yield之间有什么区别 [英] what's the difference between yield from and yield in python 3.3.2+

查看:128
本文介绍了python 3.3.2+中的yield from和yield之间有什么区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在python 3.3.2+之后,python支持创建生成器函数的新语法

After python 3.3.2+ python support a new syntax for create generator function

yield from <expression>

我已经通过

>>> def g():
...     yield from [1,2,3,4]
...
>>> for i in g():
...     print(i)
...
1
2
3
4
>>>

使用起来似乎很简单,但是 PEP 文档却很复杂.我的问题是,与之前的yield语句相比,还有其他区别吗?谢谢.

It seems simple to use but the PEP document is complex. My question is that is there any other difference compare to the previous yield statement? Thanks.

推荐答案

对于大多数应用程序,yield from只会按顺序从左开始迭代生成所有内容:

For most applications, yield from just yields everything from the left iterable in order:

def iterable1():
    yield 1
    yield 2

def iterable2():
    yield from iterable1()
    yield 3

assert list(iterable2) == [1, 2, 3]

对于90%看到这篇文章的用户,我猜这对他们来说已经足够解释了. yield from只需将委托到右侧的可迭代对象上即可.

For 90% of users who see this post, I'm guessing that this will be explanation enough for them. yield from simply delegates to the iterable on the right hand side.

但是,还有一些更深奥的生成器情况在这里也很重要.关于生成器的一个鲜为人知的事实是它们可以用作协程.这并不常见,但是您可以根据需要将数据发送到生成器:

However, there are some more esoteric generator circumstances that also have importance here. A less known fact about Generators is that they can be used as co-routines. This isn't super common, but you can send data to a generator if you want:

def coroutine():
    x = yield None
    yield 'You sent: %s' % x

c = coroutine()
next(c)
print(c.send('Hello world'))

此外:您可能想知道此用例是什么(而且您并不孤单).一个示例是contextlib.contextmanager装饰器.协同例程还可以用于并行执行某些任务.我不知道有太多利用它的地方,但是google app-engine的ndb数据存储区API以一种非常漂亮的方式将其用于异步操作.

Aside: You might be wondering what the use-case is for this (and you're not alone). One example is the contextlib.contextmanager decorator. Co-routines can also be used to parallelize certain tasks. I don't know too many places where this is taken advantage of, but google app-engine's ndb datastore API uses it for asynchronous operations in a pretty nifty way.

现在,假设您将send数据发送到一个生成器,该生成器正在从另一个生成器生成数据...如何通知原始生成器?答案是它不在python2.x中,您需要自己包装生成器:

Now, lets assume you send data to a generator that is yielding data from another generator ... How does the original generator get notified? The answer is that it doesn't in python2.x where you need to wrap the generator yourself:

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都是微不足道的:

This all becomes trivial with yield from:

def coroutine_wrapper():
    yield from coroutine()

因为yield from将(一切!)真正委托给基础生成器.

Because yield from truly delegates (everything!) to the underlying generator.

请注意,所讨论的PEP也会更改返回语义.尽管不是OP的直接问题,但如果您愿意的话,则值得快速离题.在python2.x中,您无法执行以下操作:

Note that the PEP in question also changes the return semantics. While not directly in OP's question, it's worth a quick digression if you are up for it. In python2.x, you can't do the following:

def iterable():
    yield 'foo'
    return 'done'

这是SyntaxError.随着对yield的更新,上述功能不合法.同样,主要用例是协同程序(请参见上文).您可以将数据发送到生成器,它可以神奇地完成工作(也许使用线程?),而程序的其余部分则可以完成其他工作.当流控制传递回生成器时,会升高StopIteration(这对于生成器端来说是正常的),但是现在StopIteration将具有数据有效载荷.就像程序员改写了一样:

It's a SyntaxError. With the update to yield, the above function is not legal. Again, the primary use-case is with coroutines (see above). You can send data to the generator and it can do it's work magically (maybe using threads?) while the rest of the program does other things. When flow control passes back to the generator, StopIteration will be raised (as is normal for the end of a generator), but now the StopIteration will have a data payload. It is the same thing as if a programmer instead wrote:

 raise StopIteration('done')

现在,调用者可以捕获该异常,并对数据有效载荷进行某些操作,以造福人类.

Now the caller can catch that exception and do something with the data payload to benefit the rest of humanity.

这篇关于python 3.3.2+中的yield from和yield之间有什么区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆