15

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

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

为什么我的装饰器不起作用,我该如何解决?

4

4 回答 4

8

你的递归调用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
于 2012-12-17T13:33:18.587 回答
1

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

只要去掉最后一个元素,单靠改变@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()非递归的,并将所有工作委托给另一个未修饰的生成器。后者可以是递归的。

于 2012-12-17T13:33:55.737 回答
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
于 2014-09-16T12:58:35.167 回答
0

一个更简单的解决方案。

  • 将指向初始生成器的指针保存为最终生成器的属性:
def dec(gen):

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

  new_gen.gen = gen
  return new_gen
  • 使用指向初始生成器的指针代替意外递归的生成器:
@dec
 def gen1(x):
  if x <= 10:
   yield x
   for v in gen1.gen(x+1):
    yield v
于 2021-11-14T09:21:59.417 回答