gpt4 book ai didi

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

转载 作者:IT老高 更新时间:2023-10-28 12:06:33 25 4
gpt4 key购买 nike

我很难把我的大脑包裹起来 PEP 380 .

  • “yield from”在哪些情况下有用?
  • 什么是经典用例?
  • 为什么与微线程相比?

  • [ 更新 ]

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

    恕我直言 协程是最晦涩的 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是吗在调用者和子生成器之间建立透明的双向连接 :

  • 连接是“透明的”,因为它也将正确传播所有内容,而不仅仅是正在生成的元素(例如传播异常)。
  • 从数据可以从生成器发送到生成器的意义上说,连接是“双向的”。

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

    顺便说一句,如果您不确定向生成器发送数据意味着什么,您需要先放弃所有内容并首先阅读协程——它们非常有用(将它们与子例程进行对比),但不幸的是,在 Python 中鲜为人知。 Dave Beazley's Curious Course on Coroutines是一个很好的开始。 Read slides 24-33快速入门。

    使用 yield from 从生成器读取数据
    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 将数据发送到生成器(协程) - 第 1 部分

    现在让我们做一些更有趣的事情。让我们创建一个名为 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

    包装器需要接受发送给它的数据(显然)并且还应该处理 StopIteration当 for 循环耗尽时。显然只是在做 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透明地处理向子生成器发送值或抛出值。

    尽管如此,这仍然没有涵盖所有的极端情况。如果外部发电机关闭会发生什么?当子生成器返回一个值时(是的,在 Python 3.3+ 中,生成器可以返回值),返回值应该如何传播? That yield from transparently handles all the corner cases is really impressive . yield from只是神奇地工作并处理所有这些情况。

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

    综上所述,最好是想 yield from作为 transparent two way channel 在调用者和子生成器之间。

    引用:
  • PEP 380 - 委托(delegate)给子生成器的语法 (Ewing) [v3.3, 2009-02-13]
  • PEP 342 ——
    通过增强型生成器(GvR、Eby)[v2.5, 2005-05-10] 的协程
  • 关于python - 在实践中,Python 3.3 中新的 "yield from"语法的主要用途是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9708902/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com