Этот вопрос уже имеет ответ здесь:
Что является самым эффективным способом чередовать принимающие значения от различных итераторов в Python, так, чтобы, например, alternate(xrange(1, 7, 2), xrange(2, 8, 2))
уступил бы 1, 2, 3, 4, 5, 6. Я знаю, что один способ реализовать его был бы:
def alternate(*iters):
while True:
for i in iters:
try:
yield i.next()
except StopIteration:
pass
Но есть ли более эффективный или более чистый путь? (Или, еще лучше, itertools
функционируйте я отсутствовал?)
как насчет zip? вы также можете попробовать izip из itertools
>>> zip(xrange(1, 7, 2),xrange(2, 8 , 2))
[(1, 2), (3, 4), (5, 6)]
, если это не то, что вы хотите, пожалуйста, приведите больше примеров в вашем вопросе.
Для "чистой" реализации вы хотите
itertools.chain(*itertools.izip(*iters))
, но, возможно, вы хотите
itertools.chain(*itertools.izip_longest(*iters))
Если они одинаковой длины, itertools.izip можно использовать следующим образом:
def alternate(*iters):
for row in itertools.izip(*iters):
for i in row:
yield i
См. roundrobin
в разделе itertools
"Recipes". Это более общая версия альтернативы.
def roundrobin(*iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
# Recipe credited to George Sakkis
pending = len(iterables)
nexts = cycle(iter(it).__next__ for it in iterables)
while pending:
try:
for next in nexts:
yield next()
except StopIteration:
pending -= 1
nexts = cycle(islice(nexts, pending))
Можно определить alternate
следующим образом:
import itertools
def alternate(*iters):
for elt in itertools.chain.from_iterable(
itertools.izip(*iters)):
yield elt
print list(alternate(xrange(1, 7, 2), xrange(2, 8, 2)))
Это оставляет открытым вопрос о том, что делать, если один итератор остановится раньше другого.
Если вы хотите продолжить до тех пор, пока не исчерпается самый длинный итератор, то можно использовать itertools.izip_longest
вместо itertools.izip
.
import itertools
def alternate(*iters):
for elt in itertools.chain.from_iterable(
itertools.izip_longest(*iters)):
yield elt
print list(alternate(xrange(1, 7, 2), xrange(2, 10, 2)))
Это поставит урожайность
[1, 2, 3, 4, 5, 6, None, 8]
Примечание Нет урожайности
, когда xrange итератора(1,7,2) поднимает StopIteration (не имеет больше элементов).
Если бы вы хотели просто пропустить итератор вместо того, чтобы выдавать None
, вы могли бы сделать это:
Dummy=object()
def alternate(*iters):
for elt in itertools.chain.from_iterable(
itertools.izip_longest(*iters,fillvalue=Dummy)):
if elt is not Dummy:
yield elt