如何在Python中创建一个重复生成器,比如xrange?例如,如果我这样做:
>>> m = xrange(5)
>>> print list(m)
>>> print list(m)
我两次都得到相同的结果-数字0..4。但是,如果我在yield上尝试同样的方法:
>>> def myxrange(n):
... i = 0
... while i < n:
... yield i
... i += 1
>>> m = myxrange(5)
>>> print list(m)
>>> print list(m)
第二次尝试在m上迭代时,我没有得到任何结果-一个空列表。
有没有一个简单的方法来创建一个重复的生成器,比如xrange和yield,或者生成器理解?我找到了a workaround on a Python tracker issue,它使用decorator将生成器转换为迭代器。每次开始使用时都会重新启动,即使上次没有使用所有值,就像xrange一样。我还提出了自己的decorator,基于同样的想法,它实际上返回了一个生成器,但是在抛出stopietition异常后可以重新启动:
@decorator.decorator
def eternal(genfunc, *args, **kwargs):
class _iterable:
iter = None
def __iter__(self): return self
def next(self, *nargs, **nkwargs):
self.iter = self.iter or genfunc(*args, **kwargs):
try:
return self.iter.next(*nargs, **nkwargs)
except StopIteration:
self.iter = None
raise
return _iterable()
有没有更好的方法来解决这个问题,只使用产量和/或发电机的理解?或者是Python内置的东西?所以我不需要自己的班级和装修师?
那个comment by u0b34a0f6ae找出了我误解的根源:
xrange(5) does not return an iterator, it creates an xrange object. xrange objects can be iterated, just like dictionaries, more than once.
我的“external”函数像迭代器/生成器(__iter__
返回self)而不是像collection/xrange(__iter__
返回一个新迭代器)那样,完全是在错误的树上乱吠。
不是直接的。允许生成器用于实现共同例程、资源管理等的灵活性的一部分是,它们总是一次性的。一旦运行,发电机就不能重新运行。您必须创建一个新的生成器对象。
但是,您可以创建自己的类来重写
__iter__()
。它就像一个可重复使用的发电机:如果你写了很多这样的东西,约翰·米利金的答案是最干净的。
但是,如果您不介意添加3行和一些缩进,则可以在不使用自定义装饰器的情况下完成此操作。这包含两个技巧:
[通常有用:]不需要实现
.next()
-只需使用一个用于__iter__(self)
的生成器!您可以在函数中定义一个一次性类,而不是使用构造函数。
=>
小贴士:我没有测试性能,生成这样的类可能是浪费。但是太棒了;-)
使用itertools非常简单。
相关问题 更多 >
编程相关推荐