Python 重新启动使用可重新填充迭代器的生成器

Python 重新启动使用可重新填充迭代器的生成器,python,generator,Python,Generator,我在使用使用可重新填充迭代器的生成器时遇到问题 这是我的简单生成器: def hi(iterable): for val in iterable: yield val 我传递到hi生成器中的iterable是来自的库类,它可以在耗尽其元素后重新填充 我想使用hi生成器,直到StopIteration被引发,然后重新填充iterable,然后像这样再次使用它 refillable = Reservoir((1, 2, 3, 4)) hi_iter = hi(refillable)

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

这是我的简单生成器:

def hi(iterable):
  for val in iterable:
    yield val
我传递到hi生成器中的iterable是来自的库类,它可以在耗尽其元素后重新填充

我想使用hi生成器,直到StopIteration被引发,然后重新填充iterable,然后像这样再次使用它

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:
我无法将iterable保存到元组中,因为iterable并不总是生成相同的项,并且在第二次填充refillable之前,这些项是未知的。

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

只需使用一个元组

def hi(iterable):
    return tuple(iterable)

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

此对象将引发
StopIteration
一次,然后重新启动。它打算与
可重置的
装饰器一起使用,装饰器将
\u 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
现在,您可以执行以下操作:

@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
>g=f()
>>>下一个(g)
1.
>>>下一个(g)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“”,第8行,下一步__
文件“”,第16行,在发送中
停止迭代
>>>下一个(g)
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

我无法将iterable保存到元组,因为iterable并不总是生成相同的项,并且在第二次填充refillable之前,这些项是未知的。感谢您的输入!我喜欢这有装饰器的方式,可以使用任何发电机。几年后,在忘记了它之后,回到这一点:我想我今天的实现会略有不同。但是我认为创建一个包装器类和一个使用它的装饰器可能仍然是最好的解决方案。不幸的是,我不能在大多数情况下重新定义我将使用的生成器。