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

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

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

让我们先解决一件事。

yield from g
等同的解释
for v in g: yield v
甚至还没有开始对
yieldfrom
所有内容 进行公正 处理。因为,让我们面对现实,如果
yieldfrom
要做的就是扩大
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' % idef reader_wrapper(g):    # Manually iterate over data produced by reader    for v in g:        yield vwrap = reader_wrapper(reader())for i in wrap:    print(i)# Result<< 0<< 1<< 2<< 3

无需手动迭代

reader()
,我们可以
yield from
做到。

def reader_wrapper(g):    yield from g

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

使用第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    passw = writer()wrap = writer_wrapper(w)wrap.send(None)  # "prime" the coroutinefor 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
句柄a遇到一个
SpamException
,它将打印
***

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

如果我们不改变

writer_wrapper
怎么办?它行得通吗?我们试试吧

# writer_wrapper same as abovew = writer()wrap = writer_wrapper(w)wrap.send(None)  # "prime" the coroutinefor 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>>  2Traceback (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+中,生成器可以返回值),该如何处理?这

yieldfrom
透明地处理所有角落的情况下确实是令人印象深刻。
yield from
只是神奇地工作并处理了所有这些情况。

我个人认为这

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

总之,最好将其

yield from
视为
transparent two way channel
调用方和子生成方之间的。

参考文献:

  1. PEP 380-委派给子发电机的语法(Ewing)[v3.3,2009-02-13]
  2. PEP 342-通过增强型生成器进行协同程序(GvR,Eby)[v2.5,2005-05-10]


欢迎分享,转载请注明来源:内存溢出

原文地址:https://www.54852.com/zaji/5666840.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-12-17
下一篇2022-12-16

发表评论

登录后才能评论

评论列表(0条)

    保存