Как украсить генератор в 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

почему мой оформитель не работает и как я могу это исправить?

3 ответов


рекурсивный вызов вашей 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

он не работает из-за взаимодействия между декоратором и рекурсии. Поскольку ваш генератор рекурсивен, он полагается на определенное рекуррентное отношение. Путем введения модифицирующей оформитель между генератором и суб-генератора, вы нарушаете, что рекуррентное соотношение.

пока @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() нерекурсивный, и делегировать всю работу другому, не украшенному, генератору. Последнее может быть рекурсивным.


мое решение, когда я должен был сделать sth как это было, чтобы создать генератор на генератор! Это на самом деле идея украшенного звонка. Так и есть,

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