首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在实践中,Python3.3中新的“what from”语法的主要用途是什么?

在实践中,Python3.3中新的“what from”语法的主要用途是什么?
EN

Stack Overflow用户
提问于 2012-03-15 03:33:42
回答 7查看 168.7K关注 0票数 536

我很难把我的大脑围绕在PEP 380上。

  1. 在哪些情况下“收益来自”是有用的?
  2. 经典用例是什么?
  3. 为什么它与micro-threads?

相比?

更新

现在我明白我遇到困难的原因了。我使用过生成器,但从未真正使用过协程(由PEP-342引入)。尽管有一些相似之处,生成器和协程基本上是两个不同的概念。理解协程(不仅仅是生成器)是理解新语法的关键。

协程是Python语言中最晦涩难懂的特性,大多数书籍都让它显得无用和无趣。

感谢你的精彩回答,但特别感谢agf和他链接到David Beazley presentations的评论。大卫很棒。

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2014-09-30 05:22:58

让我们先做一件事。yield from g等同于for v in g: yield v 的解释甚至不能公正地解释关于yield from的所有内容。因为,让我们面对它,如果yield from所做的只是扩展for循环,那么它并不保证将yield from添加到语言中,并且排除了一大堆新特性在Python2.x中的实现。

yield from所做的是它在调用者和子生成器之间建立一个透明的双向连接

(如果我们谈论的是TCP,yield from g可能意味着“现在暂时断开客户端的套接字,并将其重新连接到另一个服务器套接字”。)

顺便说一句,如果你不确定发送数据到生成器是什么意思,你需要先放下所有的东西,阅读关于协程的知识-它们非常有用(与子例程对比),但不幸的是,在Python中鲜为人知。Dave Beazley's Curious Course on Coroutines是一个很好的开始。快速入门的Read slides 24-33

使用yield从生成器读取数据

代码语言:javascript
复制
def reader():
    """A generator that fakes a read from a file, socket, etc."""
    for i in range(4):
        yield '<< %s' % i

def reader_wrapper(g):
    # Manually iterate over data produced by reader
    for v in g:
        yield v

wrap = reader_wrapper(reader())
for i in wrap:
    print(i)

# Result
<< 0
<< 1
<< 2
<< 3

我们可以直接yield from它,而不是手动迭代它。

代码语言:javascript
复制
def reader_wrapper(g):
    yield from g

这样就行了,我们去掉了一行代码。而且其意图可能会更清晰一些(或者不清楚)。但生活没有任何改变。

使用yield from将数据发送到生成器(协程)-第1部分

现在让我们做一些更有趣的事情。让我们创建一个名为writer的协程,它接受发送给它的数据并写入套接字、fd等。

代码语言:javascript
复制
def writer():
    """A coroutine that writes data *sent* to it to fd, socket, etc."""
    while True:
        w = (yield)
        print('>> ', w)

现在的问题是,包装器函数应该如何处理向编写器发送数据,以便发送到包装器的任何数据都透明地发送到writer()

代码语言:javascript
复制
def writer_wrapper(coro):
    # TBD
    pass

w = writer()
wrap = writer_wrapper(w)
wrap.send(None)  # "prime" the coroutine
for i in range(4):
    wrap.send(i)

# Expected result
>>  0
>>  1
>>  2
>>  3

包装器需要接受发送给它的数据(显然),并且还应该在for循环耗尽时处理StopIteration。显然,只做for x in coro: yield x是不行的。这是一个可以工作的版本。

代码语言:javascript
复制
def writer_wrapper(coro):
    coro.send(None)  # prime the coro
    while True:
        try:
            x = (yield)  # Capture the value that's sent
            coro.send(x)  # and pass it to the writer
        except StopIteration:
            pass

或者,我们可以这样做。

代码语言:javascript
复制
def writer_wrapper(coro):
    yield from coro

这节省了6行代码,使它更具可读性,而且它可以正常工作。魔术!

将数据发送到生成器产生-第2部分-异常处理

让我们把它变得更复杂。如果我们的编写器需要处理异常怎么办?假设writer处理一个SpamException,如果遇到***,它会打印出来。

代码语言:javascript
复制
class SpamException(Exception):
    pass

def writer():
    while True:
        try:
            w = (yield)
        except SpamException:
            print('***')
        else:
            print('>> ', w)

如果我们不改变writer_wrapper呢?它起作用了吗?让我们试一下

代码语言:javascript
复制
# writer_wrapper same as above

w = writer()
wrap = writer_wrapper(w)
wrap.send(None)  # "prime" the coroutine
for i in [0, 1, 2, 'spam', 4]:
    if i == 'spam':
        wrap.throw(SpamException)
    else:
        wrap.send(i)

# Expected Result
>>  0
>>  1
>>  2
***
>>  4

# Actual Result
>>  0
>>  1
>>  2
Traceback (most recent call last):
  ... redacted ...
  File ... in writer_wrapper
    x = (yield)
__main__.SpamException

嗯,它不能工作,因为x = (yield)只是引发了异常,所有的东西都崩溃了。让我们让它工作,但手动处理异常并发送它们或将它们抛入子生成器(writer)

代码语言:javascript
复制
def writer_wrapper(coro):
    """Works. Manually catches exceptions and throws them"""
    coro.send(None)  # prime the coro
    while True:
        try:
            try:
                x = (yield)
            except Exception as e:   # This catches the SpamException
                coro.throw(e)
            else:
                coro.send(x)
        except StopIteration:
            pass

这是可行的。

代码语言:javascript
复制
# Result
>>  0
>>  1
>>  2
***
>>  4

但这也是如此!

代码语言:javascript
复制
def writer_wrapper(coro):
    yield from coro

yield from透明地处理发送值或将值抛入子生成器。

然而,这仍然不能涵盖所有的角落案例。如果外部发电机关闭,会发生什么情况?如果子生成器返回一个值(是的,在Python 3.3+中,生成器可以返回值),应该如何传播返回值呢?That yield from transparently handles all the corner cases is really impressiveyield from可以神奇地工作并处理所有这些情况。

我个人认为yield from是一个糟糕的关键字选择,因为它没有明显的双向性质。还提出了其他关键字(如delegate ),但被拒绝了,因为向语言中添加新关键字比组合现有关键字要困难得多。

总之,最好将yield from看作调用者和子生成器之间的transparent two way channel

参考文献:

  1. PEP 380 -通过增强型生成器(GvR、Eby) v2.5委派给子生成器(Ewing) v3.3、2009-02-13
  2. PEP 342 -协程的语法,2005-05-10
票数 783
EN

Stack Overflow用户

发布于 2016-12-27 23:47:37

一个简短的例子将帮助您理解yield from的一个用例:从另一个生成器获取值

代码语言:javascript
复制
def flatten(sequence):
    """flatten a multi level list or something
    >>> list(flatten([1, [2], 3]))
    [1, 2, 3]
    >>> list(flatten([1, [2], [3, [4]]]))
    [1, 2, 3, 4]
    """
    for element in sequence:
        if hasattr(element, '__iter__'):
            yield from flatten(element)
        else:
            yield element

print(list(flatten([1, [2], [3, [4]]])))
票数 34
EN

Stack Overflow用户

发布于 2018-08-27 05:05:08

Asynchronous IO coroutine的实际应用中,yield from的行为类似于coroutine function中的await。这两个函数都用于暂停协程的执行。

generator-based coroutine.

对于Asyncio,如果不需要支持较旧的Python版本(例如,>3.5),async def/await是定义协程的推荐语法。因此,在协程中不再需要yield from

但一般而言,在asyncio之外,yield from <sub-generator>在迭代sub-generator时仍有一些其他用法,如前面的答案所述。

票数 10
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9708902

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档