如何在Python中制作重复生成器

Ali*_*ell 20 python

你如何在Python中创建像xrange这样的重复生成器?例如,如果我这样做:

>>> m = xrange(5)
>>> print list(m)
>>> print list(m)
Run Code Online (Sandbox Code Playgroud)

我两次得到相同的结果 - 数字0..4.但是,如果我尝试使用yield:

>>> def myxrange(n):
...   i = 0
...   while i < n:
...     yield i
...     i += 1
>>> m = myxrange(5)
>>> print list(m)
>>> print list(m)
Run Code Online (Sandbox Code Playgroud)

我第二次尝试迭代m,我什么都没回来 - 一个空列表.

有没有一种简单的方法可以创建像xrange那样的带有yield或生成器理解的重复生成器?我找到了Python跟踪器问题的解决方法,该问题使用装饰器将生成器转换为迭代器.每次开始使用它时都会重新启动,即使您上次没有使用所有值,就像xrange一样.我也提出了我自己的装饰器,基于相同的想法,实际上返回一个生成器,但一个可以在抛出StopIteration异常后重新启动:

@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()
Run Code Online (Sandbox Code Playgroud)

是否有更好的方法来解决问题,仅使用产量和/或发电机理解?或者内置于Python中的东西?所以我不需要滚动自己的类和装饰器?

更新

u0b34a0f6ae评论指出了我误解的根源:

xrange(5)不返回迭代器,它创建一个xrange对象.xrange对象可以像字典一样迭代,不止一次.

我的"永恒"函数完全是通过像迭代器/生成器(__iter__返回self)而不是像集合/ xrange(__iter__返回一个新的迭代器)那样吠叫错误的树.

Joh*_*kin 17

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

但是,您可以创建自己的类来覆盖__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)
Run Code Online (Sandbox Code Playgroud)

  • "解决办法".没有那么多.xrange(5)不返回迭代器,它创建一个xrange对象.xrange对象可以像字典一样迭代,不止一次. (2认同)

Mat*_*t S 11

使用 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!
Run Code Online (Sandbox Code Playgroud)

  • 不过,这会导致“打印列表(m)”(问题中的一个要求)永远运行:/ (2认同)