如何用python装饰生成器

2024-09-28 23:15:40 发布

您现在位置:Python中文网/ 问答频道 /正文

所以,我定义了一个简单的生成器:

def gen1(x):
    if x <= 10:
        yield x
        for v in gen1(x + 1):
            yield v

基本上,我想对它进行修饰,使其返回所有值,但最后一个值:

def dec(gen):

    def new_gen(x):
        g = gen(x)
        value = g.next()
        for v in g:
            yield value
            value = v

    return new_gen

现在,如果我重新定义gen1

@dec
def gen1(x):
    ...

for i in gen1(1):
    print i    # Nothing printed

但如果我使用:

some_gen = dec(gen1)

for i in some_gen(1):
    print i    # Prints 1 to 9, as needed

为什么我的装潢师不工作,我该如何修复?


Tags: innewforreturnif定义valuedef
3条回答

gen1的递归调用也受decorator的约束,因此decorator将使用所有内容。

最简单的修复方法是以非递归样式编写生成器,或封装递归:

封装:

@dec
def gen1(x):
    def inner(x):
        if x <= 10:
            yield x
            for v in inner(x + 1):
                yield v
    return inner(x)

非递归:

@dec
def gen1(x):
    for v in range(x, 11):
        yield v

由于decorator和递归之间的交互,它无法工作。因为生成器是递归的,所以它依赖于某种递归关系。通过在生成器和子生成器之间注入一个修改的decorator,您将破坏该递归关系。

只要@dec删除最后一个元素,就不能通过单独更改@dec使其与gen1()兼容。

但是,您可以更改gen1(),使其与@dec兼容:

def dec(gen):
    def new_gen(x):
        g = gen(x)
        value = g.next()
        for v in g:
            yield value
            value = v
    return new_gen

@dec
def gen1(x):
    def gen2(x):
        if x <= 10:
            yield x
            for v in gen2(x + 1):
                yield v
    for v in gen2(x):
        yield v

for i in gen1(1):
    print i    # Prints 1 to 9, as needed

这里的技巧是使gen1()非递归,并将所有工作委托给另一个未修饰的生成器。后者可以是递归的。

当我不得不做这样的事情时,我的解决方案是在发电机的顶部创建一个发电机!这实际上是一个装饰电话的想法。所以你知道

def funca():
    while True:
        print "in funca"
        yield True

def dec(func):
    while True:
        print "in funcb"
        func.next()
        yield True

decfa = dec(funca())
decfa.next()
>>
 "in funcb"
 "in funca"

至于你的问题(只给出最后一个值),我会做如下事情:

def funca():
    for i in range(1,5):
        yield i

def dec2(ff):
    try:
        while True:
            val=ff.next()
    except:
        yield val

>>>dec2(funca()).next()
4

相关问题 更多 >