[英]How to decorate a generator in python
So, I defined a simple generator:所以,我定义了一个简单的生成器:
def gen1(x):
if x <= 10:
yield x
for v in gen1(x + 1):
yield v
Basically, I want to decorate it so it returns all the values, but the last:基本上,我想装饰它以便它返回所有值,但最后一个:
def dec(gen):
def new_gen(x):
g = gen(x)
value = g.next()
for v in g:
yield value
value = v
return new_gen
Now, if I redefine gen1现在,如果我重新定义 gen1
@dec
def gen1(x):
...
for i in gen1(1):
print i # Nothing printed
but if I use:但如果我使用:
some_gen = dec(gen1)
for i in some_gen(1):
print i # Prints 1 to 9, as needed
Why my decorator doesn't work and how can I fix it?为什么我的装饰器不起作用,我该如何解决?
The recursive invocation of your gen1
is also subject to your decorator, so everything gets consumed by the decorator. gen1
的递归调用也取决于你的装饰器,所以装饰器都会消耗掉所有东西。
The simplest fix is to write the generator in non-recursive style, or to encapsulate the recursion: 最简单的解决方法是以非递归样式编写生成器,或者封装递归:
@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
It doesn't work due to the interaction between the decorator and recursion. 由于装饰器和递归之间的交互,它不起作用。 Since your generator is recursive, it relies on a certain recurrence relation.
由于您的生成器是递归的,因此它依赖于某种递归关系。 By injecting a modifying decorator between the generator and the sub-generator, you are breaking that recurrence relation.
通过在生成器和子生成器之间注入修改装饰器,您将破坏该重现关系。
As long as @dec
drops the last element, you can't make it compatible with gen1()
by changing @dec
alone. 只要
@dec
删除了最后一个元素,就@dec
单独更改@dec
使它与gen1()
兼容。
You could, however, change gen1()
to make it compatible with @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
The trick here is to make gen1()
non-recursive, and to delegate all the work to another, undecorated, generator. 这里的技巧是使
gen1()
非递归,并将所有工作委托给另一个未修饰的生成器。 The latter can be recursive. 后者可以递归。
My solution when I had to do sth like that was to create a generator on top of the generator! 我不得不这样做的解决方案是在发电机顶部创建发电机! This is actually the idea of a decorated call.
这实际上是装饰电话的想法。 So you do,
所以你也是,
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"
as for exactly your problem (yielding only the last value) I would do something like: 至于你的问题(只产生最后一个值),我会做类似的事情:
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
Yet a simpler solution.还有一个更简单的解决方案。
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.