Leçon 02-4 — for et range : répéter pour chaque
1. Objectif
À la fin de cette leçon tu sauras : écrire une boucle for ; utiliser range sous ses trois formes (range(stop), range(start, stop), range(start, stop, step)) et dire EXACTEMENT quelles valeurs chacune produit ; parcourir les caractères d'une chaîne ; sortir d'une boucle avec break et sauter un tour avec continue ; et imbriquer deux boucles sans te perdre.
2. Pourquoi c'est important
Le while de la leçon 02-3 répète « tant qu'on ne sait pas quand ça s'arrête ». Mais la plupart des répétitions du vrai code sont l'inverse : « pour CHAQUE élément de ceci » — chaque nombre de 1 à 100, chaque caractère d'un texte, chaque ligne d'un fichier (⏩ niveau 5), chaque élément d'une liste (⏩ niveau 3, où le for prendra toute sa dimension). Le for est la boucle que tu écriras le plus souvent de ta vie. Et range est son compagnon — avec son piège fondateur : range(10) s'arrête à 9. La moitié des bugs « off-by-one » du monde viennent de là.
3. Explication simple
for se lit « POUR CHAQUE valeur de la séquence, fais le bloc » :
for i in range(5):
print(i)
Sortie :
0
1
2
3
4
range(5) produit les entiers de 0 à 4 — cinq valeurs, en partant de 0, et 5 n'en fait PAS partie. À chaque tour, la variable i reçoit la valeur suivante, puis le bloc s'exécute. Pas d'initialisation, pas de mise à jour, pas de condition d'arrêt à écrire : le for gère tout — c'est sa grande force sur le while quand le nombre de tours est connu.
Les trois formes de range :
| Écriture | Produit | À retenir |
|---|---|---|
range(5) | 0 1 2 3 4 | démarre à 0, s'arrête AVANT stop |
range(1, 6) | 1 2 3 4 5 | start inclus, stop exclu |
range(0, 20, 5) | 0 5 10 15 | avance de step en step |
La règle unique : start inclus, stop exclu. Pour obtenir « de 1 à 10 », il faut écrire range(1, 11).
4. Explication approfondie
a) Pourquoi stop est exclu ? Convention (héritée du monde entier de l'informatique, on la retrouvera dans le slicing des chaînes et des listes ⏩ niveaux 1-3 et 3). Elle a deux vertus : range(n) fait exactement n tours, et range(a, b) en fait b - a. Vérification systématique quand tu doutes : écris à la main la première et la dernière valeur produites. range(1, 11) → première 1, dernière 10. Trente secondes qui évitent l'erreur la plus fréquente de la leçon.
b) Le pas, y compris négatif. range(10, 0, -2) produit 10 8 6 4 2 — départ 10, on descend de 2, on s'arrête AVANT 0. Un range qui ne peut pas avancer est simplement vide : range(5, 5) ou range(1, 10, -1) ne produisent rien — zéro tour, zéro erreur (le cousin du while faux d'entrée, leçon 02-3).
c) for parcourt bien plus que range. Une chaîne de caractères est une séquence : for letter in "python": donne à letter chaque caractère, dans l'ordre. Au passage, tu peux tester l'appartenance avec l'opérateur in (le même mot que dans le for, utilisé cette fois comme comparaison) : "a" in "chat" vaut True. ⏩ Au niveau 3, listes, tuples et dictionnaires se parcourront exactement pareil — le for que tu apprends ici est déjà le bon.
d) break et continue. Deux mots-clés qui court-circuitent le déroulement, dans un for comme dans un while :
break: sortir immédiatement et définitivement de la boucle (la plus proche). Usage type : « je cherchais quelque chose, je l'ai trouvé, inutile de continuer ».continue: abandonner le tour EN COURS et passer directement au suivant. Usage type : « ce cas ne m'intéresse pas, suivant ».
À utiliser avec sobriété : une boucle truffée de break/continue devient illisible. Souvent, un if bien placé ou une meilleure condition fait plus propre. La règle de bon sens : un break par boucle, motivé, ou rien. Piège spécifique au while : un continue placé AVANT la ligne de mise à jour saute la mise à jour → boucle infinie. Dans un for, aucun risque : le passage à la valeur suivante est automatique.
e) Boucles imbriquées. Un for dans un for : la boucle intérieure s'exécute EN ENTIER pour chaque tour de l'extérieure (3 × 3 = 9 passages pour deux boucles de 3). Deux règles d'hygiène : des noms de variables distincts et parlants (row/col, pas i/j dès que possible), et savoir que break ne sort que de la boucle la plus proche — la boucle extérieure continue.
f) for ou while ? Le choix en une question : « est-ce que je sais, au moment d'écrire la boucle, sur QUOI je répète ? » Oui (n tours, chaque caractère…) → for. Non (jusqu'à ce que l'utilisateur réussisse, jusqu'à convergence…) → while. Si tu écris un while avec un compteur qui va de 0 à n, c'est un for déguisé — réécris-le.
Difficulté honnête : le for simple s'acquiert en une session. Les deux points qui demandent de la répétition : les bornes de range (fais le test première/dernière valeur jusqu'à ce qu'il soit automatique) et les boucles imbriquées (déroule-les sur papier au moins une fois). Prévois 2–3 sessions.
5. Exemples commentés
Exemple 1 — les trois formes de range
for i in range(1, 6):
print(i, end=" ") # end=" " : espace au lieu du retour à la ligne
print()
for i in range(0, 20, 5):
print(i, end=" ")
print()
for i in range(10, 0, -2):
print(i, end=" ")
print()
Sortie :
1 2 3 4 5
0 5 10 15
10 8 6 4 2
(Le end=" " est une option de print() bien pratique pour compacter les sorties de boucles — le print() seul ferme la ligne.)
Exemple 2 — accumulateur avec for : la somme de 1 à 10
total = 0
for number in range(1, 11): # 11, pas 10 : stop exclu !
total += number
print(total)
Sortie : 55. Compare avec l'exemple 2 de la leçon 02-3 : même motif accumulateur, mais plus de compteur à gérer à la main — le for s'en charge.
Exemple 3 — parcourir une chaîne : compter les voyelles
vowel_count = 0
for letter in "programmation":
if letter in "aeiou": # in : appartenance — letter est-elle dedans ?
vowel_count += 1
print(vowel_count)
Sortie : 5 (o, a, a, i, o). Un for + un if + un compteur : ce trio résout une famille entière de problèmes (« compte les X qui vérifient Y »).
Exemple 4 — break : arrêter dès qu'on a trouvé
for n in range(2, 20):
if n * n > 50:
print(f"premier carre au-dessus de 50 : {n} * {n} = {n * n}")
break # trouvé : inutile de tester 9, 10, 11...
Sortie : premier carre au-dessus de 50 : 8 * 8 = 64. Sans le break, le programme afficherait AUSSI 9, 10, … 19 — « le premier » exige de s'arrêter.
Exemple 5 — continue : sauter les cas sans intérêt
for n in range(1, 8):
if n % 2 == 0:
continue # pair : on abandonne CE tour, suivant !
print(n, end=" ")
print()
Sortie : 1 3 5 7. (Honnêtement : if n % 2 == 1: print(...) — sans continue — ferait aussi bien ici. continue gagne quand le bloc restant est long et qu'on veut éviter d'indenter dix lignes dans un if.)
Exemple 6 — boucles imbriquées : la table de multiplication
for row in range(1, 4):
for col in range(1, 4):
print(f"{row} x {col} = {row * col}")
print("---fin ligne---") # indenté UNE fois : dans row, hors de col
Sortie :
1 x 1 = 1
1 x 2 = 2
1 x 3 = 3
---fin ligne---
2 x 1 = 2
2 x 2 = 4
2 x 3 = 6
---fin ligne---
3 x 1 = 3
3 x 2 = 6
3 x 3 = 9
---fin ligne---
Pour CHAQUE row, la boucle col déroule ses trois tours. Note les trois niveaux d'indentation : le print du séparateur est dans row mais hors de col — déplace-le d'un niveau et la sortie change du tout au tout (⏩ 02-5).
6. Erreurs fréquentes
1) L'off-by-one de range — LE classique
total = 0
for n in range(1, 10): # voulait la somme de 1 à 10...
total += n
print(total) # 45 — il manque le 10 (attendu : 55)
Aucune erreur : juste un résultat faux de 10. stop est exclu : « de 1 à 10 » = range(1, 11). Antidote systématique : première valeur, dernière valeur, écrites à la main.
2) Donner un simple nombre à for
count = int(input("Combien ? "))
for i in count:
print(i)
# TypeError: 'int' object is not iterable
for a besoin d'une séquence (quelque chose à parcourir) ; un entier nu n'en est pas une. « Répète count fois » s'écrit for i in range(count):.
3) Un float dans range
half = 10 / 2 # 4.0 + rappel 01-2 : / donne TOUJOURS un float
for i in range(half):
print(i)
# TypeError: 'float' object cannot be interpreted as an integer
range exige des entiers. Le coupable est presque toujours une division / en amont : utilise // (division entière) ou repense le calcul.
4) Modifier la variable de boucle en croyant piloter la boucle
for i in range(3):
print(i)
i = 100 # sans effet sur la suite !
Sortie : 0 1 2 (sur trois lignes). Au tour suivant, le for écrase i avec la valeur suivante du range — ta réassignation est jetée. Un for ne se pilote pas de l'intérieur ; si tu as besoin de sauter des valeurs à la main, c'est un while qu'il te faut.
5) Croire que break sort de TOUTES les boucles
for row in range(3):
for col in range(3):
if col == 1:
break # ne sort QUE de la boucle col
print(row, col)
Sortie : 0 0, 1 0, 2 0 — la boucle row continue imperturbablement. break ne casse que la boucle la plus proche. (S'échapper de deux boucles d'un coup se fait proprement avec une fonction et return ⏩ niveau 4.)
6) continue avant la mise à jour d'un while
n = 0
while n < 5:
if n == 2:
continue # saute le n += 1 → n reste 2 → boucle infinie
print(n)
n += 1
Le programme gèle à n == 2 (Ctrl+C, leçon 02-3). Dans un while, continue remonte DIRECTEMENT à la condition, en sautant tout ce qui suit — y compris la mise à jour. C'est le croisement le plus dangereux entre les leçons 02-3 et 02-4 : dans un for, le même continue serait inoffensif.
7. Lire les messages d'erreur
TypeError — l'entier qu'on ne peut pas parcourir
Combien ? Traceback (most recent call last):
File "c:\Users\jtron\Claude\learnpython\loop.py", line 2, in <module>
for i in count:
^^^^^
TypeError: 'int' object is not iterable
- Dernière ligne :
is not iterable= « pas parcourable ». Le message donne le type coupable :'int'. Unfora reçu un nombre nu. - Au-dessus : les
^^^^^soulignentcount— c'est bien l'objet aprèsinqui pose problème, pas leforlui-même. - Correction quasi mécanique : envelopper dans
range(...). Tu recroiserasis not iterabletoute ta vie ; le réflexe est toujours « qu'est-ce que j'essaie de parcourir, et est-ce parcourable ? ».
TypeError — le float dans range
Traceback (most recent call last):
File "c:\Users\jtron\Claude\learnpython\table.py", line 2, in <module>
for i in range(half):
~~~~~^^^^^^
TypeError: 'float' object cannot be interpreted as an integer
- Dernière ligne :
rangevoulait un entier, il a reçu unfloat. - Le soulignement
~~~~~^^^^^^désigne l'appelrange(half): la valeur fautive esthalf. Ligne du symptôme ≠ ligne de la cause (rappel 01-2) : remonte à la création dehalf— une division/y traîne sûrement. - Deux corrections selon l'intention :
10 // 2si tu voulais un entier de bout en bout, ouint(half)si lefloatest légitime par ailleurs.
Et comme toujours au niveau 2 : l'erreur la plus fréquente de la leçon — l'off-by-one — ne produit aucun traceback. Un résultat faux de 1 ou une itération de trop/de moins doit immédiatement te faire penser : bornes de range.
8. Exercices — faciles
Règles : nouveau fichier par exercice dans
lessons/level-02-conditions-boucles/exercices/mes-reponses/, nomméex-02-4-a.py, etc. Tape tout à la main. Prédis la sortie AVANT d'exécuter.
a) Sans exécuter, écris sur papier les valeurs EXACTES produites par chacun (première, dernière, combien), puis vérifie avec une boucle print :
range(4)
range(2, 7)
range(10, 0, -3)
range(3, 3)
b) Affiche les multiples de 3 de 3 à 30 inclus, sur une ligne (3 6 9 ... 30). Deux versions : une avec le pas de range, une avec range(3, 31) + un if + continue. Vérifie que les deux sorties sont identiques — surtout la présence du 30.
c) Demande une phrase et affiche le nombre de lettres e qu'elle contient. Contrôle : elle prefere le the vert → 8.
9. Exercices — moyens
a) Ce programme doit afficher la table de 7, « de 1 à 10 ». Il tourne sans erreur mais un client se plaint : il manque une ligne. Trouve le bug SANS exécuter, dis quelle ligne manque, corrige, et donne le range exact :
for i in range(1, 10):
print(f"7 x {i} = {7 * i}")
b) Somme des nombres pairs de 1 à 100 (contrôle : 2550). Trois versions qui doivent donner le même résultat : (1) range avec pas de 2 ; (2) range(1, 101) + if ; (3) range(1, 101) + continue sur les impairs. Laquelle trouves-tu la plus lisible ? Note-le dans ton journal.
c) La pyramide : affiche
*
**
***
****
*****
avec un for et la multiplication de chaîne "*" * n (vue en 01-1 avec la bordure de la carte). Puis la version inversée (5 étoiles, puis 4, … puis 1) — attention au pas et aux bornes du range, écris première/dernière valeur avant de taper.
10. Exercices — difficiles
a) Affiche tous les nombres premiers entre 2 et 30 (un nombre premier n'est divisible que par 1 et lui-même). Sortie attendue : 2 3 5 7 11 13 17 19 23 29. Structure imposée : une boucle extérieure sur les candidats, une boucle intérieure qui cherche un diviseur, un break, et une variable bool (un « drapeau »).
- Indice 1 : pour savoir si
nest premier, essaie de le diviser par tous les entiers de 2 àn - 1: quel opérateur de 01-2 teste « divisible » ? - Indice 2 : le drapeau :
is_prime = TrueAVANT la boucle intérieure ; si un diviseur est trouvé,is_prime = Falsepuisbreak. Où doit être remis àTruele drapeau pour le candidat suivant ? - Indice 3 : squelette — pour chaque
ndansrange(2, 31):is_prime = True; pour chaqueddansrange(2, n): sin % d == 0, baisser le drapeau etbreak; après la boucle intérieure, si le drapeau est encore levé, affichern.
11. Mini-projet lié — « table de multiplication à trous »
Écris quiz_tables.py : un quiz de 5 questions de multiplication tirées au hasard, avec score final. Session type :
=== Quiz des tables ===
Question 1 : 7 x 8 ? 56
Correct !
Question 2 : 3 x 9 ? 28
Faux : 3 x 9 = 27
...
Score final : 4/5
Contraintes : un for sur range(1, 6) pour les 5 questions ; les deux facteurs viennent de random.randint(1, 10) (présenté en leçon 02-3) ; un compteur de bonnes réponses (motif de 02-3) ; la réponse de l'utilisateur est convertie en int ; le message d'erreur donne la bonne réponse ; le nombre de questions vit dans UNE variable question_count en haut du fichier, utilisée à la fois par le range et par l'affichage du score (aucun 5 en dur en double — règle DRY de la leçon 01-1). Durée cible : 30–45 min.
Extension : à la fin, si le score est parfait, affiche un message spécial ; puis (plus dur) transforme le tout : la MÊME question est reposée tant que la réponse est fausse (un while de validation DANS le for — l'imbrication des deux leçons).
12. Correction / méthode de correction
Les solutions détaillées sont dans solutions/serie-02-4-solutions.md.
Méthode : (1) finis TOUS les exercices avant d'ouvrir les solutions ; (2) compare le raisonnement, pas seulement le code ; (3) pour chaque écart, une ligne de journal : « je pensais X, en fait Y » ; (4) refais de tête l'exercice raté, le lendemain.
13. À retenir
for x in sequence:— leforgère lui-même l'avancement : ni initialisation, ni mise à jour, ni condition à écrire.range: start inclus, stop EXCLU. « De 1 à 10 » =range(1, 11). Antidote au doute : écrire première et dernière valeur à la main.- Trois formes :
range(stop),range(start, stop),range(start, stop, step)— step négatif pour descendre. - Une chaîne se parcourt caractère par caractère ;
x in steste l'appartenance. breaksort de la boucle la plus proche (et d'elle seule) ;continuepasse au tour suivant. Sobriété.continuedans unwhilepeut sauter la mise à jour → boucle infinie ; dans unfor, sans danger.for i in 5:→TypeError: 'int' object is not iterable;range(4.0)→TypeErroraussi (entiers exigés).- Nombre de tours connu →
for; inconnu →while.
14. Questions de révision
- Quelles valeurs produit
range(3, 12, 3)? Première, dernière, combien ? - Pourquoi
range(1, 10)ne suffit-il pas pour « de 1 à 10 » ? Quelle est la convention, et quel est son avantage ? - Quelle différence de comportement entre
breaketcontinue? Donne un usage légitime de chacun. - Que se passe-t-il si on réassigne la variable de boucle dans le bloc d'un
for? - Dans deux boucles imbriquées, de laquelle
breaksort-il ? - Pourquoi
continueest-il plus dangereux dans unwhileque dans unfor? forouwhile: quelle question poser pour choisir ? Donne un exemple de chaque côté.- Que produit
range(5, 5)? Etrange(1, 10, -1)? Est-ce une erreur ?
15. Checklist de compréhension
- Je donne sans exécuter la première valeur, la dernière valeur et le nombre de tours de n'importe quel
range. - J'écris « de 1 à n inclus » avec le bon stop, du premier coup.
- Je sais compter les occurrences d'un caractère dans une chaîne avec
for+if+ compteur. - Je sais expliquer pourquoi le
breakde l'erreur n°5 ne stoppe pas la boucle extérieure. - J'ai fait dérouler sur papier une double boucle 3 × 3 (l'ordre exact des 9 passages).
- Je choisis entre
foretwhileen une phrase de justification. - Le quiz des tables tourne : 5 questions aléatoires, score exact.
Si une case reste vide : refais les exercices de la section correspondante demain, AVANT d'ouvrir la leçon suivante.
16. Commit conseillé
git add lessons/level-02-conditions-boucles/exercices/mes-reponses/
git commit -m "exercises: complete lesson 02-4 for and range"
Puis, si c'est ta fin de session : entrée de journal + git push.