您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

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

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

让我们先解决一件事。yield from g等同的解释for v in g: yield v yield from所有内容 处理。因为,让我们面对现实,如果yield from要做的就是扩大for循环,那么它就不必保证会增加yield from语言并阻止在Python 2.x中实现一堆新功能

什么yield from所做的就是 :

从某种意义上说,该连接是“透明的”,它也将正确传播所有内容,而不仅仅是生成的元素(例如,传播异常)。

该连接是在意义上是“双向”的数据可以同时寄给 一个发电机。

如果我们在谈论TCP,yield from g可能意味着“现在暂时断开客户端的套接字,然后将其重新连接到该其他服务器套接字”。

顺便说一句,如果您不确定 生成器发送数据 意味着什么,则需要删除所有内容并首先阅读 协程 ,它们非常有用(将它们与 子例程进行 对比),但不幸的是在Python中鲜为人知。戴夫·比兹利(Dave Beazley)的《协程》好奇课程一个很好的开始。阅读幻灯片24-33以获得快速入门。

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

那行得通,我们消除了一行代码。意图可能会更清晰(或不太清楚)。但是生活没有改变。

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

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

现在的问题是,包装器函数应如何处理将数据发送到编写器,以便将发送到包装器的任何数据 透明地 发送到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

包装器需要(显然) 接受 发送给它的数据,并且还应处理StopIterationfor循环耗尽时的。显然只是做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行代码,使其更具可读性,并且可以正常工作。魔法!

让我们变得更加复杂。如果我们的作者需要处理异常怎么办?假设writer句柄a遇到一个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透明地处理发送值或抛出的值到副发电机。

但是,这仍然不能涵盖所有极端情况。如果外部发电机关闭,会发生什么?如果子生成器返回一个值(是的,在Python 3.3+中,生成器可以返回值),该如何处理?yield from透明地处理所有角落的情况下确实是令人印象深刻yield from只是神奇地工作并处理了所有这些情况。

我个人认为这yield from一个糟糕的关键字选择,因为它不会使 双向 性变得显而易见。还提出了其他关键字(例如,delegate但被拒绝了,因为向该语言添加新关键字比合并现有关键字要困难得多。

总之,最好将其yield from视为 调用方和子生成方之间的

参考文献:

python 2022/1/1 18:27:12 有481人围观

撰写回答


你尚未登录,登录后可以

和开发者交流问题的细节

关注并接收问题和回答的更新提醒

参与内容的编辑和改进,让解决方法与时俱进

请先登录

推荐问题


联系我
置顶