Restart a generator that consumes a reusable iterator

I have a problem using a generator that uses a reusable iterator.

Here is my simple generator:

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

The iterable that I go into the hi generator is the Reservoir class from function_pipes repo , which can be refilled after it has exhausted its elements.

I would like to use the hi generator until StopIteration is raised and then refill iterable again and then destroy it again, e.g.

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

print(tuple(hi_iter))

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

but it prints

(1, 2, 3, 4)
()

The second tuple should also be (5, 6, 7, 8).

The only solution I found for this is wrapping the hi generator with a class

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))

This solution seems a bit overkill, and I'm looking for a simpler solution. Thank you for your help.

Ptank: , , , .

+4
3

, -. EDIT: . .

StopIteration ONCE, . 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 factory , . , , .

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
+1

What, since the generator is not a Tuple, the generated generator is memoryless and read only once.

Just use a tuple

def hi(iterable):
    return tuple(iterable)
0
source

Source: https://habr.com/ru/post/1678413/


All Articles