Comment parcourir deux listes en parallèle?

865

J'ai deux itérables en Python, et je veux les parcourir par paires:

foo = (1, 2, 3)
bar = (4, 5, 6)

for (f, b) in some_iterator(foo, bar):
    print "f: ", f, "; b: ", b

Il devrait en résulter:

f: 1; b: 4
f: 2; b: 5
f: 3; b: 6

Une façon de le faire est d'itérer sur les indices:

for i in xrange(len(foo)):
    print "f: ", foo[i], "; b: ", b[i]

Mais cela me semble quelque peu impythonique. Y a-t-il une meilleure façon de le faire?

Nathan Fellman
la source

Réponses:

1354

Python 3

for f, b in zip(foo, bar):
    print(f, b)

zips'arrête lorsque le plus court de fooou bars'arrête.

En Python 3 , zip retourne un itérateur de tuples, comme itertools.izipen Python2. Pour obtenir une liste de tuples, utilisez list(zip(foo, bar)). Et pour compresser jusqu'à épuisement des deux itérateurs, vous utiliserez itertools.zip_longest .

Python 2

En Python 2 , zip retourne une liste de tuples. C'est bien quand fooet ce barn'est pas massif. S'ils sont tous les deux massifs, la formation zip(foo,bar)est une variable temporaire inutilement massive et doit être remplacée par itertools.izipou itertools.izip_longest, qui renvoie un itérateur au lieu d'une liste.

import itertools
for f,b in itertools.izip(foo,bar):
    print(f,b)
for f,b in itertools.izip_longest(foo,bar):
    print(f,b)

izips'arrête lorsque l'un fooou l' autre barest épuisé. izip_longests'arrête lorsque les deux fooet barsont épuisés. Lorsque le ou les itérateurs les plus courts sont épuisés, izip_longestdonne un tuple avec Nonela position correspondant à cet itérateur. Vous pouvez également définir un autre en fillvalueplus Nonesi vous le souhaitez. Voir ici pour l' histoire complète .


Notez également que zipet ses zipfrères semblables peuvent accepter un nombre arbitraire d'itérables comme arguments. Par exemple,

for num, cheese, color in zip([1,2,3], ['manchego', 'stilton', 'brie'], 
                              ['red', 'blue', 'green']):
    print('{} {} {}'.format(num, color, cheese))

impressions

1 red manchego
2 blue stilton
3 green brie
unutbu
la source
@unutbu Pourquoi préférerais-je la méthode OP à izipcelle (même si la izip/ zipsemble beaucoup plus propre)?
armundle
3
Vous voudrez peut-être mentionner d'abord Python 3, car il est probablement plus à l'épreuve du temps. De plus, il vaut la peine de souligner qu'en Python 3, zip () a exactement cet avantage que seul itertools.izip () avait en Python 2 et c'est donc généralement la voie à suivre.
Daniel S.
3
Puis-je vous demander de mettre à jour votre réponse pour indiquer explicitement que les fonctions zipet zipsimilaires itertoolsacceptent n'importe quel nombre d'itérables et pas seulement 2? Cette question est maintenant canonique et votre réponse est la seule qui mérite d'être mise à jour.
vaultah
Et si je veux en plus l'index i? Puis-je envelopper ce zip en énumérer?
Charlie Parker
2
@CharlieParker: Oui, vous pouvez, mais alors vous utiliseriez for i, (f, b) in enumerate(zip(foo, bar)).
unutbu
60

Vous voulez la zipfonction.

for (f,b) in zip(foo, bar):
    print "f: ", f ,"; b: ", b
Karl Guertin
la source
11
Avant Python 3.0, vous voudriez l'utiliser itertools.izipsi vous avez un grand nombre d'éléments.
Georg Schölly
15

Vous devez utiliser la fonction ' zip '. Voici un exemple à quoi peut ressembler votre propre fonction zip

def custom_zip(seq1, seq2):
    it1 = iter(seq1)
    it2 = iter(seq2)
    while True:
        yield next(it1), next(it2)
Vlad Bezden
la source
Cela n'a-t-il pas exactement le même résultat que zip(seq1, seq2)?
Niklas Mertsch
@NiklasMertsch oui, il a exactement le même résultat. Je viens de fournir un exemple à quoi ressemble la fonction zip
Vlad Bezden
1

Vous pouvez regrouper les nièmes éléments dans un tuple ou une liste en utilisant la compréhension, puis les distribuer avec une fonction de générateur.

def iterate_multi(*lists):
    for i in range(min(map(len,lists))):
        yield tuple(l[i] for l in lists)

for l1, l2, l3 in iterate_multi([1,2,3],[4,5,6],[7,8,9]):
    print(str(l1)+","+str(l2)+","+str(l3))
Don F
la source
-2

Si quelqu'un cherche quelque chose comme ça, je l'ai trouvé très simple et facile:

list_1 = ["Hello", "World"]
list_2 = [1, 2, 3]

for a,b in [(list_1, list_2)]:
    for element_a in a:
        ...
    for element_b in b:
        ...

>> Hello
World
1
2
3

Les listes seront itérées avec leur contenu complet, contrairement à zip () qui n'itère que jusqu'à la longueur minimale du contenu.

VladiC4T
la source
les downvoters devraient commenter ce qu'ils ont trouvé mauvais avec mon approche ou si cela ne convient pas comme solution possible à cette question.
VladiC4T