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