Как вы создаете повторяющийся генератор, например xrange, в Python? Например, если я это сделаю:
>>> m = xrange(5)
>>> print list(m)
>>> print list(m)
Я получаю тот же результат оба раза - числа 0..4. Однако, если я попробую то же самое с выходом:
>>> def myxrange(n):
... i = 0
... while i < n:
... yield i
... i += 1
>>> m = myxrange(5)
>>> print list(m)
>>> print list(m)
Во второй раз, когда я пытаюсь выполнить итерацию по m, я ничего не получаю - пустой список.
Есть ли простой способ создания повторяющегося генератора, такого как xrange с выходом или с пониманием генератора? Я нашел обходной путь в проблеме Python tracker, в котором используется декоратор для преобразования генератора в итератор. Это перезапускается каждый раз, когда вы начинаете использовать его, даже если вы не использовали все значения в прошлый раз, точно так же, как 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()
Есть ли лучший способ решить проблему, используя только методы получения и/или генератора? Или что-то встроенное в Python? Так что мне не нужно качать свои классы и декораторы?
Update
Комментарий от u0b34a0f6ae прибил источник моего недоразумения:
xrange (5) не возвращает итератор, он создает объект xrange. Объекты xrange могут повторяться, как словари, более одного раза.
Моя "вечная" функция полностью лаяла по неправильному дереву, действуя как итератор/генератор (__iter__
возвращает self), а не как collection/xrange (__iter__
возвращает новый итератор).