
让我们先解决一件事。
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
包装器需要(显然) 接受 发送给它的数据,并且还应处理
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行代码,使其更具可读性,并且可以正常工作。魔法!
从第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调用方和子生成方之间的。
参考文献:
- PEP 380-委派给子发电机的语法(Ewing)[v3.3,2009-02-13]
- PEP 342-通过增强型生成器进行协同程序(GvR,Eby)[v2.5,2005-05-10]
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)