Простой подход, который может работать для вас, в зависимости от ваших требований:
import itertools
def izip_cycle(*colls):
maxlen = max(len(c) if hasattr(c,'__len__') else 0 for c in colls)
g = itertools.izip(*[itertools.cycle(c) for c in colls])
for _ in range(maxlen):
yield g.next()
Первым делом он находит длину самой длинной последовательности, поэтому он знает, сколько раз повторить. Последовательности без __len__
считаются имеющими длину 0; это может быть тем, что вы хотите - если у вас есть бесконечная последовательность, вы, вероятно, хотите повторить все конечные последовательности. Хотя это не работает с конечными итераторами без длины.
Никогда мы не используем itertools.cycle
для создания циклической версии каждого итератора, а затем используем itertools.zip
, чтобы объединить их вместе.
Наконец, мы выдаем каждую запись из нашего почтового индекса, пока не дадим желаемое количество результатов.
Если вы хотите, чтобы это работало для конечного итератора без len
, нам нужно проделать большую часть работы самостоятельно:
def izip_cycle(*colls):
iters = [iter(c) for c in colls]
count = len(colls)
saved = [[] for i in range(count)]
exhausted = [False] * count
while True:
r = []
for i in range(count):
if not exhausted[i]:
try:
n = iters[i].next()
saved[i].append(n)
r.append(n)
except StopIteration:
exhausted[i] = True
if all(exhausted):
return
saved[i] = itertools.cycle(saved[i])
if exhausted[i]:
r.append(saved[i].next())
yield r
Это, по сути, расширение реализации Python itertools.cycle
в документации для работы с несколькими последовательностями. Мы сохранили элементы, которые мы видели в saved
, чтобы повторить и отследить, какие последовательности закончились в exhausted
.
Поскольку эта версия ожидает завершения всех последовательностей, если вы передадите что-то бесконечное, цикл будет продолжаться вечно.