2015-03-24 6 views
13

Ich habe eine Liste von Servern. Jeder Server hat eine Namensliste. Beispiel:Zyklus durch mehrere Liste mit itertools.cycle()

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

möchte ich pro Servernamen iterieren nicht pro Server. Zum Beispiel, nachdem Sie 'a' in ausgewählt haben, gehen Sie zu 'd' (nicht 'b') und so weiter. Wenn ich itertools.cycle() verwenden möchte, muss ich eine Liste von Servern erstellen, um durchzulaufen? Mein erwartetes Ergebnis ist ['a','d','g','b','e','h','c','f','i']. Können Sie mir ein einfaches Beispiel geben, wie man in mehreren Listen zyklisch arbeitet?

Antwort

7

Dieser funktioniert einwandfrei:

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

Beachten Sie, sind die list und islice nur zu Demonstrationszwecken etwas zu geben, unendlich Ausgabe anzuzeigen und zu verhindern, ...

, es wird interessanter, wenn man ungleich langen Listen haben. Dann wird izip_longest dein Freund sein, aber es könnte eine Funktion an diesem Punkt wert sein:

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) 

Demo:

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

Wenn Sie Listen mit unquel Länge haben, sehen Sie sich die Lösung von @ Anonymous an (http: // stackoverflow.com/a/29226491/4687645). Es ist wesentlich schneller, wenn der Unterschied in den Listenlängen groß ist. – Matt

3

Try this:

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

Aber Pflege dies bietet unendliche Schleife

Also das besser tun:

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

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

Sie können es die mit zip und reduce tun built-in Funktionen (und in 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') 

Oder statt reduce() für lange Listen Sie itertools.chain verwenden können, um die Unterlisten zu verketten, die einen Generator zurück:

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

HINWEIS, dass, wenn Sie Ihr Ergebnis iterieren wollen Sie don‘ t müssen list auf das Ergebnis chain verwenden. Sie können nur so etwas wie:

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

Benchmarking auf den vorhergehenden Rezepte:

#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 

Beachten Sie, dass, wenn Sie nicht die Server innerhalb einer Liste setzen Sie wäre es schneller:

:~$ 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 
+0

Hinweis: 'compiler.ast.flatten 'ist veraltet und die Verwendung von' itertools.chain' ist sehr wahrscheinlich die zeiteffizienteste Lösung. – Shashank

+1

@Shashank Ja Sicher, aber Kette ist gut für lange Listen und für kurze Liste wie diese "reduzieren" in besser! Schau dir das Benchmarking an! – Kasramvd

+1

Sie haben Recht. Ich stehe korrigiert. :) – Shashank

11

Wir können auch itertools.chain.from_iterable() verwenden, das ist schneller im Vergleich.

import itertools 

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

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

Ergebnisse:

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

Es ist besser, itertools.izip() zu verwenden, besonders wenn die Eingabedaten groß sind, da zip() eine Liste, nicht einen Iterator, gemäß docs: https: //docs.python.org/2/library/functions.html#zip –

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

Ich habe es als 'chain (* map (lambda * args: args, * (server1, server2, server3))' gemacht, aber ich bevorzuge deins und @Anonymous Antworten –

2

können Sie Kette verwenden:

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

Oder kürzer:

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

Ist nicht 'liste (itertools.chain.from_iterable (zip (* all_servers)))' sauberer als die verschachtelte Liste-comp? – mgilson

+1

@mgilson Ja, aber solche Antwort existiert bereits, also wollte ich nicht duplizieren Es ist seine Alternative, aber ich habe es jetzt auch hinzugefügt. – Marcin

5

Th e standard library documentation stellt diese Funktion als Rezept in itertools zur Verfügung.

Dieser Code funktioniert auch dann, wenn die Iterables ungleich lang sind und die verbleibenden durchlaufen werden, wenn die kürzeren verbraucht sind. Das kann für Ihren Anwendungsfall relevant sein oder auch nicht.

2

chain Verwenden Sie einfach das tun:

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] 


Oder Sie können chain.from_iterable verwenden, erwartet er eine iterable dass sich Iteratoren erzeugt.

In Ihrem Fall zip ist, dass iterable, es Iteratoren in Form von Tupeln erzeugt:

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


yield Auch könnte hier verwendet werden:

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]