Кеширование генератора

Недавний похожий вопрос (isinstance (foo, types.GeneratorType) или inspect.isgenerator (foo)?) мне стало интересно, как реализовать это в общем.

Кажется, что обычно полезно иметь объект типа генератора, который будет кэшироваться в первый раз (например,itertools.cycle), сообщите StopIteration, а затем верните элементы из кэша в следующий раз, но если объект неt генератор (то есть список или диктат, который по своей природе поддерживает поиск O (1)), тогда неt кэшировать, и иметь то же поведение, но для исходного списка.

Возможности:

1) Изменить itertools.cycle. Это выглядит так:

def cycle(iterable):
    saved = []
    try: 
         saved.append(iterable.next())
         yield saved[-1]
         isiter = True
    except:
         saved = iterable
         isiter = False
    # cycle('ABCD') --> A B C D A B C D A B C D ...
    for element in iterable:
        yield element
        if isiter: 
            saved.append(element)

     # ??? What next?

Если бы я мог перезапустить генератор, это было бы идеально - я мог бы отправить обратно StopIteration, а затем в следующем gen.next () вернуть запись 0, т.е. но это нене похоже на этона самом деле возможно.

Вторым было бы то, что, как только StopItered нажата, тогда сохраненный имеет кеш. Но это нене похоже тамЛюбой способ добраться до внутреннего сохраненного поля []. Может быть, классная версия этого?

2) Или я мог бы перейти в список напрямую:

def cycle(iterable, saved=[]):
    saved.clear()
    try: 
         saved.append(iterable.next())
         yield saved[-1]
         isiter = True
    except:
         saved = iterable
         isiter = False
    # cycle('ABCD') --> A B C D A B C D A B C D ...
    for element in iterable:
        yield element
        if isiter: 
            saved.append(element)

mysaved = []
myiter = cycle(someiter, mysaved)

Но это выглядит просто противно. А в C / ++ я мог бы передать некоторую ссылку и изменить фактическую ссылку на сохраненную, чтобы она указывала на повторяемость - вы можете 'на самом деле это сделать в Python. Так что это нет даже работать.

Другие опции?

Изменить: больше данных. Метод CachingIterable, кажется, слишком медленный, чтобы быть эффективным, но он подтолкнул меня в направлении, которое могло бы работать. Это'Немного медленнее, чем наивный метод (преобразующий себя в список), но, похоже, не воспринимает удар, если он 'уже итерируемо

Некоторый код и данные:

def cube_generator(max=100):
    i = 0
    while i < max:
        yield i*i*i
        i += 1

# Base case: use generator each time
%%timeit
cg = cube_generator(); [x for x in cg]
cg = cube_generator(); [x for x in cg]
cg = cube_generator(); [x for x in cg]
10000 loops, best of 3: 55.4 us per loop

# Fastest case: flatten to list, then iterate
%%timeit
cg = cube_generator()
cl = list(cg)
[x for x in cl]
[x for x in cl]
[x for x in cl]
10000 loops, best of 3: 27.4 us per loop

%%timeit
cg = cube_generator()
ci2 = CachingIterable(cg)
[x for x in ci2]
[x for x in ci2]
[x for x in ci2]
1000 loops, best of 3: 239 us per loop

# Another attempt, which is closer to the above
# Not exactly the original solution using next, but close enough i guess
class CacheGen(object):
    def __init__(self, iterable):
        if isinstance(iterable, (list, tuple, dict)):
            self._myiter = iterable
        else:
            self._myiter = list(iterable)
    def __iter__(self):
        return self._myiter.__iter__()
    def __contains__(self, key):
        return self._myiter.__contains__(key)
    def __getitem__(self, key):
        return self._myiter.__getitem__(key)

%%timeit
cg = cube_generator()
ci = CacheGen(cg)
[x for x in ci]
[x for x in ci]
[x for x in ci]
10000 loops, best of 3: 30.5 us per loop

# But if you start with a list, it is faster
cg = cube_generator()
cl = list(cg)
%%timeit
[x for x in cl]
[x for x in cl]
[x for x in cl]
100000 loops, best of 3: 11.6 us per loop

%%timeit
ci = CacheGen(cl)
[x for x in ci]
[x for x in ci]
[x for x in ci]
100000 loops, best of 3: 13.5 us per loop

Любые быстрые рецепты, которые могут стать ближе кчистый» цикл?

Ответы на вопрос(2)

Ваш ответ на вопрос