Подтвердить что ты не робот

Цикл через несколько списков, используя itertools.cycle()

У меня есть список серверов. На каждом сервере есть список имен. Пример:

server1 = ['a','b','c']
server2 = ['d','e','f']
server3 = ['g','h','i']

Я хочу, чтобы итерация по имени сервера не на сервер. Например, после выбора 'a' в server1 перейдите к 'd' (не 'b') и так далее. Если я собираюсь использовать itertools.cycle(), мне нужно создать список серверов для циклического перехода? Мой ожидаемый результат ['a','d','g','b','e','h','c','f','i']. Можете ли вы дать мне простой пример того, как циклически перемещаться по нескольким спискам.

4b9b3361

Ответ 1

Это хорошо работает:

>>> from itertools import chain, islice, izip, cycle
>>> list(islice(cycle(chain.from_iterable(izip(server1, server2, server3))), 0, 18))
['a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i', 'a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i']

Примечание. list и islice предназначены только для демонстрации, чтобы дать что-то для отображения и предотвращения бесконечного вывода...

Теперь это становится более интересным, если у вас есть неравные списки длин. Тогда izip_longest будет вашим другом, но на данный момент это может стоить функции:

import itertools
def cycle_through_servers(*server_lists):
    zipped = itertools.izip_longest(*server_lists, fillvalue=None)
    chained = itertools.chain.from_iterable(zipped)
    return itertools.cycle(s for s in chained if s is not None)

демо:

>>> from itertools import islice
>>> server3 = ['g', 'h', 'i', 'j']
>>> list(islice(cycle_through_servers(server1, server2, server3), 0, 20))
['a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i', 'j', 'a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i', 'j']

Ответ 2

Мы также можем использовать itertools.chain.from_iterable(), который быстрее сравнивается.

import itertools

server1 = ['a','b','c']
server2 = ['d','e','f']
server3 = ['g','h','i']

print list(itertools.chain.from_iterable(zip(server1,server2,server3)))

Результаты:

['a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i']

Ответ 3

Вы можете сделать это с помощью zip и reduce встроенных функций (и в python3 functools.reduce):

>>> list_of_servers=[server1,server2,server3]
>>> s=reduce(lambda x,y:x+y,zip(*list_of_servers))
>>> s
('a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i')

Или вместо reduce() для длинных списков вы можете использовать itertools.chain для конкатенации подписок, возвращающих генератор:

>>> list(chain(*zip(*[server1,server2,server3])))
['a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i']

Обратите внимание: если вы хотите перебрать результат, вам не нужно использовать list для результата chain. Вы можете просто сделать что-то вроде:

for element in chain(*zip(*[server1,server2,server3])):
     #do stuff

Бенчмаркинг по предыдущим рецептам:

#reduce()
:~$ python -m timeit "server1 = ['a','b','c'];server2 = ['d','e','f'];server3 = ['g','h','i'];reduce(lambda x,y:x+y,zip(*[server1,server2,server3]))"
1000000 loops, best of 3: 1.11 usec per loop
#itertools.chain()
:~$ python -m timeit "server1 = ['a','b','c'];server2 = ['d','e','f'];server3 = ['g','h','i'];from itertools import chain;chain(*zip(*[server1,server2,server3]))"
100000 loops, best of 3: 2.02 usec per loop

Обратите внимание, что если вы не поместите серверы в список, это будет быстрее:

:~$ python -m timeit "server1 = ['a','b','c'];server2 = ['d','e','f'];server3 = ['g','h','i'];reduce(lambda x,y:x+y,zip(server1,server2,server3))"
1000000 loops, best of 3: 0.98 usec per loop

Ответ 4

Документация стандартной документации предоставляет эту функцию в качестве рецепта в itertools.

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

Этот код работает даже тогда, когда итераторы имеют неравномерную длину, перемещаясь по оставшимся, когда более короткие используются. Это может быть или не иметь отношения к вашему прецеденту.

Ответ 5

Попробуйте следующее:

from itertools import cycle
for k in cycle([j for i in zip(server1,server2,server3) for j in i]):
   print(k)
   #do you operations

a
d
g
b
...

Но уход обеспечивает бесконечный цикл

Так лучше сделайте это:

c = cycle([j for i in zip(server1,server2,server3) for j in i])

>>>next(c)
a
>>>next(c)
b
....

Ответ 6

from itertools import chain
for s in chain(*zip(server1, server2, server3)):
    # do work

Ответ 7

Вы можете использовать цепочку:

import itertools

server1 = ['a','b','c']
server2 = ['d','e','f']
server3 = ['g','h','i']


all_servers = [server1, server2, server3] 

out_list = [s_name for a in itertools.chain(zip(*all_servers)) for s_name in a]

print(out_list)
#['a', 'd', 'g', 'b', 'e', 'h', 'c', 'f', 'i']

Или короче:

out_list = list(itertools.chain.from_iterable(zip(*all_servers)))

Ответ 8

Используя chain, вы можете просто сделать это:

from itertools import chain, izip
server1 = [1, 2]
server2 = [3, 4]
server3 = [4, 5]
print list(chain(*izip(server1, server2, server3))) # [1, 3, 4, 2, 4, 5]


Или вы можете использовать chain.from_iterable, он ожидает, что итерабель сам генерирует итераторы.

В вашем случае zip является итерабельным, он генерирует итераторы в виде кортежей:

print list(chain.from_iterable(zip(server1, server2, server3))) # [1, 3, 4, 2, 4, 5]


yield тоже можно использовать здесь:

def f():
    server1 = [1, 2]
    server2 = [3, 4]
    server3 = [4, 5]
    for a, b, c in zip(server1, server2, server3):
        yield a
        yield b
        yield c

val = f()
print [val.next() for _ in range(6)] # [1, 3, 4, 2, 4, 5]