Python中的yield from语法

来源:互联网 发布:网络监控软件哪个好用 编辑:程序博客网 时间:2024/05/16 15:21

这篇文章主要是翻译stackoverflow上的一篇文章,In practice, what are the main uses for the new “yield from” syntax in Python 3.3? 加上自己的一点删改和想法

首先要注意yield from 提供了一种透明的双向通道从callersubgenerator,即可以发送数据到subgenerator或者从subgenerator读取数据

Reading data from a generator using yield from

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

我们可以使用yield from g来替换for v in g: yield v这种方式来重新改写reader_wrapper

def reader_wrapper(g):    yield from g

我们可以减少一行代码,而且编码的意图更加清晰。但是yield from并没有引入新的功能

Sending data to a generator(coroutine) using yield from– Part 1

如果不明白怎么发送数据,可以去看Dave Beazley’s Curious Course on Couroutines 上的slides的24-33页。
如果我们把之前使用yield生成数据的叫做generator,那么使用yield读取数据的叫做coroutine(协程)。coroutine和generator是毫无关系的,不要将这两个概念混淆。generator produce data for iteration, coroutine are consumers of data.另外coroutine和iteration是无关的,即使有时候coroutine会使用yield生成数据,但是这也和iteration是无关的。

先创建一个名叫writer的coroutine接受data并写入到file, socket等等

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

接着来考虑如何编写装饰器wrapper发送data给writer使得发送给装饰器wrapper的data都可以透明地传给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

writer_wrapper需要接收传给它的数据,并且处理StopIteration异常。下面是一个可以满足要轻松的writer_wrapper

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

这样可以节省6行代码,并且可读性更强。

Sending data to a generator(coroutine) using yield from – Part 2 – Exception handling

我们现在考虑的更加复杂,让writer可以处理异常,当writer遇到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

可以看出来不能处理SpamException,因为x = (yield)只是抛出异常并没有处理。下边修改writer_wrapper,让其处理或者throw该异常到subgenerator

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透明地把dataException传给了subgenerator

以上并没有覆盖所有的case。比如当外部的generator关闭会发生什么,subgenerator return一个值时怎么将return的值传递,yield from都可以处理所有情况。 All the corner cases that yield from handles transparently is really impressive
yield from其实不太能够反映它的双向的特性,有其他的一些关键字被提议例如delegate但是因为程序语言增加一个关键字比联合现有的关键字更加麻烦,所以采用了yield from

一定要记住yield fromcallersubgenerator之间的透明双向通道

In summary, it’s best to think yield from as a transparent two-way channel between the caller and the subgenerator.

0 0
原创粉丝点击