如何在python中装饰生成器

时间:2012-12-17 13:29:54

标签: python generator decorator

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

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

为什么我的装饰工作不起作用以及如何解决?

3 个答案:

答案 0 :(得分:5)

你的gen1的递归调用也取决于你的装饰者,所以一切都被装饰者消耗了。

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

封装:

@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

答案 1 :(得分:0)

由于装饰器和递归之间的交互,它不起作用。由于您的生成器是递归的,因此它依赖于某种递归关系。通过在生成器和子生成器之间注入修改装饰器,可以打破该重现关系。

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

但是,您可以更改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()非递归,并将所有工作委托给另一个未修饰的生成器。后者可以是递归的。

答案 2 :(得分:0)

我必须这样做的解决方案就是在发电机顶部创建发电机!这实际上是装饰电话的想法。所以你这样做,

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