La fonction zip() est apparue dans Python 2 (elle retournait alors une liste), et depuis Python 3, elle retourne un itérateur paresseux. Elle "assemble" plusieurs séquences en tuples élément par élément, ce qui a rendu le traitement de collections itérables parallèles pratique et efficace.
Il est souvent nécessaire de traiter plusieurs listes (ou d'autres types de séquences) simultanément — par exemple, parcourir une paire clé-valeur ou traiter les coordonnées d'un point. La synchronisation manuelle des index est une source d'erreurs et de manque de lisibilité du code, surtout pour les collections de longueurs différentes.
La fonction zip() accepte n'importe quel nombre d'objets itérables et retourne un itérateur de tuples, chacun contenant les éléments correspondants de chaque itérable. Si les séquences sont de longueurs différentes, le résultat s'arrête à la plus courte.
names = ['Alice', 'Bob', 'Charlie'] ages = [24, 27, 30] for name, age in zip(names, ages): print(f'{name} a {age} ans')
Il est possible de décompresser zip à l'aide de * :
pairs = [(1, 'a'), (2, 'b'), (3, 'c')] nums, chars = zip(*pairs) print(nums) # (1, 2, 3) print(chars) # ('a', 'b', 'c')
Que se passe-t-il si vous passez à zip() des collections de longueurs différentes ?
zip() s'arrêtera lorsque la fin de la plus courte collection est atteinte — les autres éléments des collections plus longues seront ignorés.
print(list(zip([1,2,3], ['a','b']))) # [(1, 'a'), (2, 'b')]
Comment obtenir des tuples en complétant les séquences plus courtes avec une valeur par défaut ?
La zip() standard ne supporte pas cela, mais il existe itertools.zip_longest pour ce comportement :
from itertools import zip_longest for a, b in zip_longest([1,2], ['x','y','z'], fillvalue=None): print(a, b) # 1 x # 2 y # None z
Peut-on "déballer" le résultat de zip() pour retrouver les listes d'origine ?
Oui, si toutes les collections d'origine avaient la même longueur et que le résultat n'a pas été modifié, l'opérateur * permet de décompresser zip.
pairs = [(1,2), (3,4)] a, b = zip(*pairs) print(a) # (1, 3) print(b) # (2, 4)
Traitement de collections liées de longueurs différentes sans tenir compte des spécificités de zip :
lst1 = [1,2,3,4] lst2 = ['a','b'] for x, y in zip(lst1, lst2): print(x, y) # 1 a # 2 b # (3,4) et 'c', 'd' de lst1 n'ont pas été traités
Avantages :
Inconvénients :
Utilisation de zip_longest avec fillvalue, pour ne perdre aucun élément :
from itertools import zip_longest lst1 = [1,2,3,4] lst2 = ['a','b'] for x, y in zip_longest(lst1, lst2, fillvalue='?'): print(x, y) # 1 a # 2 b # 3 ? # 4 ?
Avantages :
Inconvénients :