Range, Enumerate et Zip
Générez des séquences de nombres, suivez les indices et combinez des itérables.
- Utiliser range() pour générer des séquences de nombres
- Utiliser enumerate() pour obtenir l'indice et la valeur pendant l'itération
- Utiliser zip() pour itérer sur plusieurs séquences en parallèle
- Écrire des boucles pythoniques qui évitent le suivi manuel d'indices
Trois outils qui supplantent le comptage manuel
Les boucles vous ont donné la répétition ; cette leçon vous remet les trois aides qui vous ôtent le comptage des mains. Chacun remplace une habitude qu’on vous a apprise à la main, et chacun répond à une irritation récurrente : générer des nombres, avoir besoin de la position d’un élément, et apparier deux listes. Ensemble, ils font la différence entre une boucle qui tape et une boucle qui se lit.
Range : la séquence arithmétique, paresseuse
Dans la leçon précédente vous avez sommée avec range(5). Cet outil mérite un regard attentif, c’est l’outil classique du « fais ceci un nombre connu de fois » :
for i in range(5):
print(i) # 0 1 2 3 4range a trois formes, miroir de la progression arithmétique :
range(5) # 0, 1, 2, 3, 4
range(2, 8) # 2, 3, 4, 5, 6, 7
range(0, 20, 3) # 0, 3, 6, 9, 12, 15, 18Un argument donne ; deux donnent l’intervalle semi-ouvert ; trois ajoutent la différence commune . Surtout, range est paresseux : il enregistre les paramètres et calcule chaque valeur seulement quand la boucle la demande. Demander un million de pas ne coûte pas plus de mémoire que d’en demander cinq, la séquence n’est jamais matérialisée.
Enumerate : la position, sans le compteur
Vous voulez la position de chaque élément ? L’instinct de débutant est un compteur manuel :
fruits = ["apple", "banana", "cherry"]
i = 0
for fruit in fruits:
print(f"{i}: {fruit}")
i += 1Le i += 1 est une tentation à se désynchroniser : oubliez-en un, et les étiquettes de position s’embrouillent. enumerate produit les deux moitiés en une étape, l’indice et l’élément, de sorte qu’il n’y a rien à synchroniser :
for i, fruit in enumerate(fruits):
print(f"{i}: {fruit}")
# Les sondeurs numérotent les gens à partir de 1 :
for i, fruit in enumerate(fruits, start=1):
print(f"{i}: {fruit}")Là où un mathématicien écrit pour attacher la position à la valeur, enumerate remet la paire directement au corps de la boucle.
Zip : un alignement par position
Deux listes parallèles, noms et scores, réclament d’être lues ensemble. zip les aligne élément par élément :
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
for name, score in zip(names, scores):
print(f"{name}: {score}")
# Alice: 85
# Bob: 92
# Charlie: 78L’appariement est le truc cartésien de courir le long des deux listes avec un seul curseur, formant les tuples . Quand les listes diffèrent en longueur, l’appariement s’arrête à la plus courte, si bien que rien n’est jamais à demi apparié. S’il vous faut aussi la queue tordue, itertools.zip_longest la comble :
import itertools
for pair in itertools.zip_longest([1, 2], [3, 4, 5], fillvalue=0):
print(pair) # (1, 3), (2, 4), (0, 5) — aucune valeur n'est perdueUn exemple travaillé : le registre de la classe
Voyez les trois outils composer ensemble. Un enseignant tient une liste de noms et une liste parallèle de notes, et veut un rapport numéroté :
names = ["Dina", "Omar", "Sara"]
scores = [78, 91, 85]
for i, (name, score) in enumerate(zip(names, scores), start=1):
print(f"#{i} {name}: {score}")
# #1 Dina: 78
# #2 Omar: 91
# #3 Sara: 85
print(f"Top score: {max(scores)}") # Top score: 91Lisez l’en-tête de boucle de l’intérieur vers l’extérieur : zip apparie chaque nom à sa note ; les parenthèses (name, score) déballent cette paire ; enumerate numérote les paires à partir de un. Quatre gestes qui vous auraient coûté un compteur écrit à la main se lisent désormais comme la phrase qu’ils décrivent, la position s’attache à la valeur, paire par paire, exactement comme attache un indice à chaque terme.
Pièges courants
rangeest exclusif en haut.range(5)produit , cinq nombres, aucun égal à . Pensez intervalle semi-ouvert, .enumeratesur un dict. Itérer un dict donne ses clés ;enumeratenuméroterait les clés, pas les paires. Utilisezdict.items()pour la clé et la valeur.zipavec des longueurs inégales. Les éléments au-delà de l’entrée courte s’évanouissent en silence. Notez la perte, ou comblez aveczip_longest.zipest un itérateur à usage unique. En Python 3,p = zip(a, b)vous tend un itérateur, pas une liste :list(p)le consomme, et un secondlist(p)est vide. Convertissez sans tarder aveclist(zip(a, b))quand vous revisiterez les paires.
🧩 Défis
🧩 Défi, réfléchissez d'abord, puis révélez
Utilisez enumerate pour imprimer chaque couleur de colors = ["red", "green", "blue"] avec sa position commençant à 1.
💡 Réponse : for i, color in enumerate(colors, 1): print(f"{i}. {color}"), l'argument start renumérote les paires à partir de un.
🧩 Défi, réfléchissez d'abord, puis révélez
Avec keys = ["a", "b"] et values = [1, 2], utilisez zip pour construire un dictionnaire.
💡 Réponse : dict(zip(keys, values)) → {"a": 1, "b": 2}, les paires alignées deviennent les entrées du mapping.
🤔 Questions socratiques
- Pourquoi préférer
rangeà écrire la liste[0, 1, 2, 3, 4]? Qu’est-ce qui change si la liste devait contenir un million de nombres ? - Puisque
zips’arrête à l’entrée la plus courte, comment détecteriez-vous quel côté était le court ? Quand cette distinction importe-t-elle ? enumeratepeut-il parcourir un dict ? Que numérotent exactement les indices ?
✅ Vérification rapide
1. Que vaut list(range(1, 10, 2)) ?
2. Que renvoie list(zip([1, 2], [3, 4, 5])) ?