ProgrammationDéveloppeur Backend

Comment fonctionne la fonction intégrée zip() en Python, à quoi sert-elle et quels sont les points délicats à prendre en compte lors du traitement de séquences de longueurs différentes ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Histoire de la question

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.

Problème

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.

Solution

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.

Exemple de code :

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

Caractéristiques clés :

  • zip() retourne un itérateur (en Python 3), et non une liste.
  • Le fonctionnement de zip() s'arrête à l'itérable le plus court.
  • Permet le traitement parallèle de collections sans contrôle explicite des index.

Questions pièges.

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)

Erreurs typiques et anti-patterns

  • S'attendre à ce que zip() atteigne toujours la fin de la plus longue collection.
  • Supposer qu'en Python 3, zip() retourne une liste (c'est un itérateur, parfois il faut l'encapsuler dans list()).
  • Travailler avec zip sur des sources modifiables, qui sont épuisées à chaque itération.

Exemple pratique

Cas négatif

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 :

  • Simple et clair, si les séquences sont garantie d'être de longueur identique.

Inconvénients :

  • Perte de valeurs si la longueur réelle des collections diffère.

Cas positif

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 :

  • Garantie de traitement de tous les éléments.
  • Possibilité de spécifier explicitement une valeur "vide".

Inconvénients :

  • Nécessité d'importer un module externe.
  • Important de ne pas oublier fillvalue, sinon — None par défaut.