重新启动消耗可重新填充迭代器的生成器

问题描述 投票:0回答:4

我在使用使用可重新填充迭代器的生成器时遇到问题。

这是我的简单生成器:

def hi(iterable):
  for val in iterable:
    yield val

我传递到 hi 生成器的可迭代对象是来自 function_pipes 存储库的 Reservoir 类,它可以在耗尽其元素后重新填充。

我想使用 hi 生成器,直到引发 StopIteration,然后重新填充可迭代对象,然后再次使用它,就像

refillable = Reservoir((1, 2, 3, 4))
hi_iter = hi(refillable)

print(tuple(hi_iter))

refillable((5, 6, 7, 8))
print(tuple(hi_iter))

但是这个打印

(1, 2, 3, 4)
()

第二个元组也应该是(5,6,7,8)。

我找到的唯一解决方案是用类包装 hi 生成器

def super_gener(function):
  class wrapper_class:
    def __init__(self, iterable):
      self.iterable = iterable
      self.zipped = None

    def __iter__(self):
      return self

    def __next__(self):
      try:
        return next(self.zipped)

      except TypeError:
        self.zipped = function(self.iterable)
        return next(self)

      except StopIteration as err:
        self.zipped = None
        raise err

  return wrapper_class

hi_iter = super_gener(hi)(refillable)

print(tuple(hi_iter))
refillable(data)
print(tuple(hi_iter))

这个解决方案似乎有点过多,我正在寻找一个更简单的解决方案。感谢您的帮助。

回复Ptank: 我无法将可迭代对象保存到元组中,因为可迭代对象并不总是产生相同的项目,并且在第二次填充可重新填充之前这些项目是未知的。

python generator
4个回答
3
投票

恐怕唯一的解决方案可能是创建一个可重新填充的生成器包装类。编辑:原始未经测试的代码不起作用。我现在重构了下面的想法并对其进行了测试。

此对象将升高

StopIteration
一次,之后它将重新启动。它旨在与
Resettable
装饰器一起使用,该装饰器向类添加
_func
属性。它应该具有与原始生成器相同的所有功能。

class ResettableGenerator():
    '''Generator wrapper that is resettable.'''
    def __init__(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs
        self.start()
    def __next__(self):
        n = self.send(None)
        return n
    def __iter__(self):
        yield from self._gen
    def start(self):
        self._gen = self._func(*self.args, **self.kwargs)
    def send(self, *args, **kwargs):
        try:
            n = self._gen.send(*args, **kwargs)
            return n
        except StopIteration:
            self.start()
            raise
    def throw(self, *args, **kwargs):
        self._gen.throw(*args, **kwargs)
    def close(self):
        self._gen.close()

这是装饰器:

def Resettable(some_func):
    cls = type(some_func.__name__, (ResettableGenerator,), {})
    cls._func = staticmethod(some_func)
    return cls

像这样使用它:

@Resettable
def f():
    yield 1

现在你可以做这样的事情:

>>> g=f()
>>> next(g)
1
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __next__
  File "<stdin>", line 16, in send
StopIteration
>>> next(g)
1 #  generator has restarted itself

1
投票

您也许可以将

hi
更新为生成器工厂,并在每次需要使用时调用它。这比您当前的解决方案更干净,但由于其他原因可能不适用于您的情况。

def hi(iterable):
    def wrapper():
        for val in iterable:
            yield val
    return wrapper

与您当前使用的用法类似:

refillable = Reservoir((1, 2, 3, 4))
hi_iter = hi(refillable)
print(tuple(hi_iter())) # (1, 2, 3, 4)

refillable((5, 6, 7, 8))
print(tuple(hi_iter())) # (5, 6, 7, 8)

如果您想与原始示例保持完全相同的语法,您可以创建一个类似的瘦类。

class hi:
    def __init__(self, iterable):
        self.__iter__ = lambda: iterable

0
投票

因为生成器不是元组,所以生成的生成器是无记忆的并且只能读取一次。

只需使用元组

def hi(iterable):
    return tuple(iterable)

0
投票

我也遇到过和你类似的情况。不同之处在于,就我而言,生成器本身会处理迭代器的重新填充。

def getRandomWord():
   words = RandomWords().get_random_words()
   print("before yield")
   for w in words:
       print("yielding")
       yield w
   print("here")
   for w in getRandomWord():
       yield w

这将无限地生成单词。 words 变量是一个单词列表,当它用完时,函数调用自身生成另一个列表并不断生成单词。希望这对您和新人有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.