如何在Python中创建重复生成器

2024-05-17 12:50:10 发布

您现在位置:Python中文网/ 问答频道 /正文

如何在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__返回一个新迭代器)那样,完全是在错误的树上乱吠。


Tags: 方法selfreturndefargsdecoratoriterablekwargs
3条回答

不是直接的。允许生成器用于实现共同例程、资源管理等的灵活性的一部分是,它们总是一次性的。一旦运行,发电机就不能重新运行。您必须创建一个新的生成器对象。

但是,您可以创建自己的类来重写__iter__()。它就像一个可重复使用的发电机:

def multigen(gen_func):
    class _multigen(object):
        def __init__(self, *args, **kwargs):
            self.__args = args
            self.__kwargs = kwargs
        def __iter__(self):
            return gen_func(*self.__args, **self.__kwargs)
    return _multigen

@multigen
def myxrange(n):
   i = 0
   while i < n:
     yield i
     i += 1
m = myxrange(5)
print list(m)
print list(m)

如果你写了很多这样的东西,约翰·米利金的答案是最干净的。

但是,如果您不介意添加3行和一些缩进,则可以在不使用自定义装饰器的情况下完成此操作。这包含两个技巧:

  1. [通常有用:]不需要实现 .next()-只需使用一个用于__iter__(self)的生成器!

  2. 您可以在函数中定义一个一次性类,而不是使用构造函数。

=>

def myxrange(n):
    class Iterable(object):
        def __iter__(self):
            i = 0
            while i < n:
                yield i
                i += 1
    return Iterable()

小贴士:我没有测试性能,生成这样的类可能是浪费。但是太棒了;-)

使用itertools非常简单。

import itertools

alist = [1,2,3]
repeatingGenerator = itertools.cycle(alist)

print(next(generatorInstance)) #=> yields 1
print(next(generatorInstance)) #=> yields 2
print(next(generatorInstance)) #=> yields 3
print(next(generatorInstance)) #=> yields 1 again!

相关问题 更多 >