使用itertools.cycle()循环遍历多个列表

时间:2015-03-24 06:37:30

标签: python itertools

我有一个服务器列表。每个服务器上都有一个名称列表。 例如:

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']。你能给我一个关于如何在多个列表中循环的简单示例。

8 个答案:

答案 0 :(得分:11)

我们也可以使用比较快的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']

答案 1 :(得分:9)

您可以使用zipreduce内置函数(以及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

答案 2 :(得分:7)

这个工作正常:

>>> 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']

注意,listislice仅用于演示目的,以显示内容并防止无限输出......

现在,如果您有不等长度列表,它会变得更有趣。那么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']

答案 3 :(得分:5)

standard library documentation将此功能作为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))

这个代码即使在迭代的长度不均匀时也能工作,当较短的迭代用完时,循环通过剩余的代码。这可能与您的用例相关,也可能不相关。

答案 4 :(得分:3)

试试这个:

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

答案 5 :(得分:3)

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

答案 6 :(得分:2)

您可以使用链:

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

答案 7 :(得分:2)

使用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]