在实践中,Python 3.3中新的“yield from”语法的主要用途是什么?

问题:

这个问题一直困扰着我啊。。PEP 380

  1. “yield”是什么情况有用?
  2. 什么是经典用例?
  3. 为什么比较微线程呢?

[更新]
现在我明白我困难的原因。我使用过生成器,但从来没有真正使用过协同程序(由PEP-342引入)。尽管有一些相似之处,生成器和协同工作基本上是两个不同的概念。了解协程(不仅是生成器)是理解新语法的关键。
IMHO 协同程序是最晦涩的Python功能,大多数书籍看起来都没用,没趣味。
感谢您的精彩答案,但特别感谢agf及其与David Beazley presentations相关的评论。大卫岩石

回答:

让我们先来看一件事。 yield from g相当于for v in g: yield v 甚至没有开始正义yield from的一切解释。因为,让我们面对它,如果所有yield from都是扩展for循环,那么它不保证添加yield from到语言,并排除了一大堆新功能在Python 2.x中的实现。
yield from设为从callersub-generator透明双向通道。这包括getting data fromsending data to sub-generator。 BTW,如果您不确定sending data to a generator甚至意味着什么,您需要先删除所有内容并阅读有关协程序。 Dave Beazley’s Curious Course on Couroutines是一个很好的开始。 Read slides 24-33快速入门。

使用yield从生成器/generator读取数据

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

而不是手动迭代reader(),我们只能yield from它。

def reader_wrapper(g):
    yield from g

这有效,我们删除了一行代码。也许这个意图有点清楚(或不)。但没有什么生活改变。

第一部分 – 使用yield from向生成器(协程)发送数据

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

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

现在的问题是,包装器函数应该如何处理向写入器发送数据,以便将发送到包装器的任何数据transparently发送到writer()

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

包装器需要accept发送给它的数据(显然),并且当for循环耗尽时也应该处理StopIteration。显然只是做for x in coro: yield x不行。这是一个有效的版本。

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

或者,我们可以这样做。

def writer_wrapper(coro):
    yield from coro

这可以节省6行代码,使其更易于阅读,并且可以正常工作。魔法!

第2部分 – 异常处理时,向生成器发送数据

让我们让它更复杂如果我们的作者需要处理异常怎么办?假设writer处理一个SpamException,如果它遇到一个,打印***

class SpamException(Exception):
    pass

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

如果我们不改变writer_wrapper怎么办?它工作吗?咱们试试吧

# 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

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

这个工作。

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

但是这样呢

def writer_wrapper(coro):
    yield from coro

yield from透明地处理将值发送到子生成器中。
这仍然不包括所有的角落。如果外部的生成器关闭会发生什么?当子生成器返回值(yes,在python 3中,生成器可以返回值)的情况如何,返回值应如何传播? All the corner cases that yield from handles transparently is really impressiveyield from只是神奇地工作和处理所有这些情况。
我个人觉得yield from是一个糟糕的关键字选择,因为它不会使two-way性质显而易见。还有其他提出的关键字(如delegate但被拒绝),因为向该语言添加新关键字要比组合现有关键字要困难得多。
总之,最好将yield from视为callersub-generator之间的
参考文献:

  1.  PEP 380 – 委托给子生成器的语法(Ewing)[v3.3,2009-02-13]
  2.  PEP 342
    通过增强型生成器(GvR,Eby)[V2.5,2005-05-10]

Code问答: http://codewenda.com/topics/python/
Stackoverflow: In practice, what are the main uses for the new “yield from” syntax in Python 3.3?

*转载请注明本文链接以及stackoverflow的英文链接

发表评论

电子邮件地址不会被公开。 必填项已用*标注

99 − = 95