La boucle for
Prédire la valeur
Écris la valeur de l'expression demandée comme Python l'écrirait ('abc', True, 2.0), puis valide. Quand le champ est marqué « affiche », écris plutôt ce que le programme affiche. Si le programme s'arrête sur une erreur, écris erreur. Les valeurs changent à chaque essai.
Rappel d'ouverture (5 minutes, cours fermé)
Réponds sans rouvrir les pages précédentes, en écrivant tes réponses.
- Si
accvaut10, que vautaccaprèsacc = acc + 3? Et dans quel ordre la machine procède-t-elle ? - Avec
mot = "python": que valentmot[0],mot[-1]etlen(mot)? Quel est le plus grand indice valide ? - La fonction
double(n)renvoie le double den. Écris l'assertqui vérifie quedouble(3)vaut6. Que se passe-t-il s'il est faux ?
Correction
13. Le membre de droite est calculé d'abord (10 + 3), puis rangé dansacc. C'est ce mécanisme, répété, qui va faire toute la page d'aujourd'hui."p","n"et6. Le plus grand indice valide est5, soitlen(mot) - 1: écriremot[6]lève uneIndexError.assert double(3) == 6. S'il est vrai, rien ne se passe ; s'il est faux, Python lève uneAssertionErroret arrête le programme.
L'idée : pour chaque élément d'un itérable
Faire l'appel en classe, c'est parcourir la classe : pour chaque élève, on applique la même procédure.
Itérable et séquence
Un itérable est un objet dont on peut obtenir les éléments un par un. C'est exactement ce dont la boucle for a besoin, et rien de plus.
Une séquence est un itérable particulier : c'est une suite ordonnée d'éléments. Comme l'ordre compte, on peut numéroter les éléments, et chacun a un indice.
Tu en connais déjà un : la chaîne de caractères (str). C'est un itérable, puisqu'on en obtient les caractères un par un. C'est aussi une séquence : "chat" est la suite "c", "h", "a", "t", dans cet ordre, "chat"[0] vaut "c", et "tahc" est une autre chaîne.
Toutes les séquences sont des itérables, mais tous les itérables ne sont pas des séquences. La classe de l'appel en est un exemple : c'est un itérable, puisqu'on en appelle les élèves un par un, mais ce n'est pas une séquence. Il n'existe aucun ordre entre les élèves, et faire l'appel dans le désordre produit exactement le même effet. En Python, tu rencontreras de même les dictionnaires, que le for parcourt aussi, mais dont les éléments ne se repèrent pas par un indice.
Parcourir la classe élève par élève, c'est exactement ce que fait la boucle for en Python, sur un itérable. Son idée tient en une phrase :
La boucle for
Pour chaque élément, qu'on appelle x, d'un itérable, répéter un bloc d'instructions (avec cette valeur de x).
À chaque tour, x prend la valeur de l'élément suivant de l'itérable. Le bloc doit être indenté.
Une boucle for consiste donc à appliquer le même traitement à chaque élément d'un itérable.
c, puis h, puis a, puis t : un tour par caractère. On applique donc le même traitement (afficher) à chaque caractère de "chat".
Pour aller plus loin : caractère ou graphème ?
Pour Python, un élément d'une str est un caractère Unicode. Ce n'est pas toujours ce qu'un lecteur voit comme une « lettre » : l'unité d'écriture que l'œil perçoit s'appelle un graphème, et un graphème peut être formé de plusieurs caractères.
Le mot "বাংলা" (bangla, le nom de la langue bengalie) se lit en deux graphèmes, বাং et লা. Pourtant :
fait cinq tours, et len("বাংলা") vaut 5 :
| Tour | c |
ce que c'est |
|---|---|---|
| 1 | ব |
la lettre ba |
| 2 | া |
le signe de voyelle aa |
| 3 | ং |
le signe anusvara (son « ng ») |
| 4 | ল |
la lettre la |
| 5 | া |
le signe de voyelle aa |
Les signes des tours 2, 3 et 5 ne s'écrivent jamais seuls : ils se combinent avec la lettre qui les précède. Affichés isolément, ils apparaissent souvent accolés à un cercle pointillé.
La boucle for parcourt donc les caractères, pas les graphèmes. La question se pose aussi en français, plus discrètement : "é" peut s'écrire avec un seul caractère, ou avec un e suivi d'un accent combinant, et len donne alors 1 ou 2 pour le même graphème.
>>> a = "é" # un seul caractère : « e accent aigu »
>>> b = "e" + chr(769) # deux caractères : « e », puis l'accent aigu combinant (code 769)
>>> print(a)
é
>>> print(b)
é
>>> len(a)
1
>>> len(b)
2
>>> a == b
False
Les deux chaînes s'affichent é, et pourtant Python les déclare différentes : il compare des suites de caractères, pas ce que l'œil voit.
Ce que fait la machine, tour par tour
Déroulons for x in "abc": avec, dans le corps, print(x) :
| Tour | x prend la valeur |
ce qui s'affiche |
|---|---|---|
| 1 | "a" |
a |
| 2 | "b" |
b |
| 3 | "c" |
c |
Quand il n'y a plus d'élément, la boucle s'arrête. Le point clé, c'est de savoir ce que vaut x à chaque tour.
L'accumulation : le motif fondamental
Ce qui guide tout ce qu'on va faire en programmation
Ce qui suit n'est pas une technique parmi d'autres : c'est le motif qui guide tout ce qu'on va faire en programmation. Compter, sommer, filtrer, chercher le plus petit, construire une chaîne, puis une liste, puis un dictionnaire : c'est chaque fois une accumulation.
Face à un problème, la question à se poser est donc toujours la même : comment me ramener à une accumulation ?
La plupart des problèmes se résolvent en construisant progressivement un résultat dans une variable appelée l'accumulateur.
Exemple : doubler chaque lettre d'un texte, "chat" devenant "cchhaatt".
- On crée l'accumulateur. Le résultat est une chaîne, elle vaut
""au départ (on n'a encore rien construit) : - On parcourt, un par un, chaque caractère du texte :
- À chaque caractère rencontré, on l'ajoute deux fois au résultat :
- À la fin de la boucle, tous les caractères ont été traités :
Méthodologie de l'accumulation
- Je réfléchis au type de mon résultat.
- Je me demande ce que vaut mon accumulateur quand je n'ai pas encore commencé le traitement.
- Qu'est-ce que je dois parcourir ?
- J'applique le comportement d'accumulation à chaque élément.
Ce mot, tu l'as déjà rencontré
Dans le Little Man Computer, l'accumulateur est la seule case de calcul du processeur, et ADD 15 fait ACC ← ACC + mémoire[15] : on prend ce qu'il y a dans l'accumulateur, on y ajoute quelque chose, et on range le résultat au même endroit.
acc = acc + car est exactement ce geste. Ce n'est pas une coïncidence de vocabulaire : le processeur lui-même est construit autour de lui.
Lire et prédire avant d'écrire
Avant d'écrire une boucle, entraîne-toi à lire celles des autres et à prédire leur résultat. C'est la meilleure préparation à en écrire soi-même.
Prédire, puis modifier
- Prédis l'affichage.
- Modifie une seule ligne pour que le mot s'affiche à l'envers.
Premiers exercices : accumuler en parcourant une chaîne
Les indices sont faits pour être ouverts
Ouvrir un indice n'est pas tricher, et ce n'est pas non plus un aveu. Ce qui coûte, ce n'est pas de demander de l'aide, c'est de rester bloqué vingt minutes sans rien produire, ou d'ouvrir la solution sans avoir compris ce qui bloquait.
La seule règle : quand un indice te débloque, écris en une phrase ce qu'il t'a appris sur ton erreur avant de continuer. C'est cette phrase qui reste, pas la solution recopiée.
1 - Afficher un par un
Avec mot = "bonjour", affiche les caractères de mot un par un, un par ligne.
2 - Tripler
Avec mot = "chat", construis dans acc la chaîne où chaque caractère est répété trois fois, puis affiche acc. On doit obtenir ccchhhaaattt.
Indice léger
C'est l'exemple « doubler » du cours, à un détail près. Qu'est-ce qui change, et qu'est-ce qui ne change pas ?
3 - Renverser, de mémoire
Sans remonter au bloc « Prédire, puis modifier », écris le programme qui construit dans acc la chaîne mot à l'envers, avec mot = "python", puis affiche acc.
Indice léger
Dans acc, où doit aller chaque nouveau caractère : avant ou après ceux qui y sont déjà ?
4 - Compter
Avec mot = "anticonstitutionnellement", compte les caractères de mot sans utiliser len, puis affiche le résultat.
- Avant la première itération de la boucle, combien de caractères as-tu comptés ? C'est la valeur de départ de ton compteur.
- Écris le programme, puis vérifie ton résultat avec
len(mot).
Indice léger
Cette fois, l'accumulateur n'est pas une chaîne mais un entier : à chaque caractère, il augmente de 1.
Correction
- Zéro : tant qu'on n'a parcouru aucun caractère, on n'en a compté aucun. C'est pour cela que le compteur part de
0. python mot = "anticonstitutionnellement" compteur = 0 for car in mot: compteur = compteur + 1 print(compteur) # 25, comme len(mot)
Itérer de manière conditionnelle
Jusqu'ici, chaque chapitre apportait sa notion : les fonctions, puis les conditionnelles, puis les boucles. Dans un vrai programme, on ne choisit pas l'une ou l'autre : on a besoin de tout en même temps. On les combine donc à la fin de chaque chapitre, et c'est ce qu'on te demande réellement de savoir faire.
Souvent, on n'accumule que les éléments qui remplissent une condition : on place alors un branchement conditionnel (un if, voir Les conditionnelles) à l'intérieur de la boucle.
Que fait ce programme ?
Lis ce programme, puis écris sur ton cahier :
- ce qu'il affiche ;
- une phrase qui dit ce qu'il fait, sans parler de Python : à quelle question répond-il ?
N'exécute pas ce code tant que tu n'as pas écrit tes réponses.
Correction
Il affiche 4. Il compte combien de fois le chiffre 6 apparaît dans la chaîne : si x note une série de lancers de dé, il dit combien de fois le 6 est sorti.
Les noms x, n et c ne t'aidaient pas : il fallait lire le code. Avec de bons noms, lancers, nb_six et de, la phrase se serait presque écrite toute seule. C'est à cela que sert un nom.
Sans les espaces
Avec phrase = "le petit chat", construis dans acc la phrase privée de tous ses espaces, puis affiche acc. On doit obtenir lepetitchat.
Indice léger
Reprends les quatre étapes de la méthodologie. Le filtre ne change ni l'initialisation ni le parcours : il ne change que ce qui se passe dans la boucle.
Indice plus précis
Un accumulateur acc = "" avant, un parcours for car in phrase, et dans la boucle un if car != " " avant d'ajouter.
Avant d'ouvrir la correction
Écris une phrase sur ton cahier : qu'est-ce que l'indice t'a appris sur ce qui bloquait dans ton code ?
Une phrase suffit, et elle doit parler de ton code, pas du cours. C'est ce geste qui fait la différence entre finir l'exercice et savoir le refaire seul la prochaine fois.
Remplacer un caractère
Avec phrase = "bonjour le monde", construis dans acc la phrase où chaque "o" est remplacé par un "0" (le chiffre zéro), puis affiche acc. On doit obtenir b0nj0ur le m0nde.
Indice léger
Cette fois, chaque caractère va dans acc. La condition ne décide pas si on ajoute, elle décide quoi ajouter.
Indice plus précis
Dans la boucle, un if car == "o" qui ajoute "0", et un else qui ajoute car tel quel.
Avant d'ouvrir la correction
Écris une phrase sur ton cahier : qu'est-ce que l'indice t'a appris sur ce qui bloquait dans ton code ?
Objectif final : tout dans des fonctions
1 - Nombre de voyelles
On veut compter : que vaut le résultat au départ ? Complète aussi le type de retour.
2 - Contient (sans l'opérateur in)
3 - Première lettre la plus petite (sans la fonction min)
Les caractères se comparent avec < selon l'ordre alphabétique : "a" < "b" vaut True.
def plus_petite_lettre(txt: str) -> str:
"""Renvoie la plus petite lettre de txt, qui n'est pas vide.
>>> plus_petite_lettre("python")
'h'
>>> plus_petite_lettre("a")
'a'
"""
assert len(txt) > 0, "le texte ne doit pas être vide"
...
Indice léger
L'accumulateur n'est pas un compteur ici : c'est la meilleure valeur rencontrée jusqu'ici. Par quoi l'initialiser ? Surtout pas par "", qui serait plus petit que tout.
Indice plus précis
On l'initialise avec txt[0], le seul candidat dont on soit sûr qu'il appartient au texte. C'est d'ailleurs à cela que sert l'assert : sans lui, txt[0] planterait sur une chaîne vide.
Correction
Tu retrouveras exactement cet algorithme sur les listes, puis sur les dictionnaires. Ce n'est pas trois algorithmes, c'est le même.4 - Tout reprendre en fonctions
Reprends les exercices de la page qui construisent un résultat et transforme chacun en fonction : le texte devient un paramètre, et le résultat est renvoyé au lieu d'être affiché. Pour chaque fonction, écris la signature et la docstring, puis complète sa fonction de test, et seulement ensuite le code.
def tripler(txt: str) -> str:
"""Renvoie txt où chaque caractère est répété trois fois."""
...
def test_tripler():
assert tripler("chat") == "ccchhhaaattt"
...
def renverser(txt: str) -> str:
"""Renvoie txt à l'envers."""
...
def test_renverser():
assert renverser("python") == "nohtyp"
...
def longueur(txt: str) -> int:
"""Renvoie le nombre de caractères de txt, sans utiliser len."""
...
def test_longueur():
...
def sans_espaces(txt: str) -> str:
"""Renvoie txt privé de tous ses espaces."""
...
def test_sans_espaces():
...
def remplacer(txt: str, ancien: str, nouveau: str) -> str:
"""Renvoie txt où chaque caractère ancien est remplacé par nouveau."""
...
def test_remplacer():
assert remplacer("bonjour le monde", "o", "0") == "b0nj0ur le m0nde"
...
Correction
def tripler(txt: str) -> str:
"""Renvoie txt où chaque caractère est répété trois fois."""
acc = ""
for car in txt:
acc = acc + car + car + car
return acc
def test_tripler():
assert tripler("chat") == "ccchhhaaattt"
assert tripler("") == ""
def renverser(txt: str) -> str:
"""Renvoie txt à l'envers."""
acc = ""
for car in txt:
acc = car + acc
return acc
def test_renverser():
assert renverser("python") == "nohtyp"
assert renverser("") == ""
assert renverser("a") == "a"
def longueur(txt: str) -> int:
"""Renvoie le nombre de caractères de txt, sans utiliser len."""
compteur = 0
for car in txt:
compteur = compteur + 1
return compteur
def test_longueur():
assert longueur("anticonstitutionnellement") == 25
assert longueur("") == 0
def sans_espaces(txt: str) -> str:
"""Renvoie txt privé de tous ses espaces."""
acc = ""
for car in txt:
if car != " ":
acc = acc + car
return acc
def test_sans_espaces():
assert sans_espaces("le petit chat") == "lepetitchat"
assert sans_espaces("") == ""
assert sans_espaces(" ") == ""
def remplacer(txt: str, ancien: str, nouveau: str) -> str:
"""Renvoie txt où chaque caractère ancien est remplacé par nouveau."""
acc = ""
for car in txt:
if car == ancien:
acc = acc + nouveau
else:
acc = acc + car
return acc
def test_remplacer():
assert remplacer("bonjour le monde", "o", "0") == "b0nj0ur le m0nde"
assert remplacer("chat", "z", "0") == "chat"
assert remplacer("", "o", "0") == ""
Exercice d'application
Problème : le chiffrement de César
Jules César chiffrait ses messages en décalant chaque lettre de trois rangs dans l'alphabet : a devient d, b devient e, et ainsi de suite. Arrivé au bout, on repart du début : x devient a, y devient b, z devient c.
- Sur ton cahier, à la main : chiffre
"zebre"avec un décalage de3. - Écris
decaler(car, n), qui décale une lettre minuscule denrangs. - Écris
cesar(txt, n), qui chiffre un texte. Seules les lettres minuscules non accentuées sont décalées, les autres caractères restent tels quels. - Quel appel de
cesarpermet de déchiffrer un message chiffré avec un décalage de3?
Rappels : ord(car) donne le code d'un caractère, chr fait l'inverse, et les codes de "a" à "z" se suivent.
ALPHABET = "abcdefghijklmnopqrstuvwxyz"
def decaler(car: str, n: int) -> str:
"""Renvoie la lettre minuscule car décalée de n rangs dans l'alphabet,
en repartant de "a" après "z".
Précondition : car est une lettre de ALPHABET."""
...
def test_decaler():
assert decaler("a", 3) == "d"
assert decaler("z", 3) == "c"
...
def cesar(txt: str, n: int) -> str:
"""Renvoie txt où chaque lettre de ALPHABET est décalée de n rangs,
les autres caractères restant inchangés."""
...
def test_cesar():
assert cesar("ave cesar, morituri te salutant", 3) == "dyh fhvdu, prulwxul wh vdoxwdqw"
...
Indice léger
Pour decaler, travaille sur le rang de la lettre dans l'alphabet, de 0 pour a à 25 pour z, plutôt que sur son code. Le rang s'obtient en soustrayant ord("a"). Pour revenir au début après z, pense au reste de la division par 26.
Indice plus précis
Le rang décalé vaut (ord(car) - ord("a") + n) % 26, et la lettre correspondante s'obtient avec chr(... + ord("a")). cesar est une accumulation dans une chaîne avec un branchement conditionnel : si car in ALPHABET, on ajoute decaler(car, n), sinon on ajoute car. C'est l'exercice « Remplacer un caractère », avec une fonction à la place du "0".
Avant d'ouvrir la correction
Écris une phrase sur ton cahier : qu'est-ce que l'indice t'a appris sur ce qui bloquait dans ton code ?
Correction
1. "cheuh".
2. et 3.
ALPHABET = "abcdefghijklmnopqrstuvwxyz"
def decaler(car: str, n: int) -> str:
"""Renvoie la lettre minuscule car décalée de n rangs dans l'alphabet,
en repartant de "a" après "z".
Précondition : car est une lettre de ALPHABET."""
assert car in ALPHABET, "car doit être une lettre minuscule non accentuée"
rang = (ord(car) - ord("a") + n) % 26
return chr(rang + ord("a"))
def test_decaler():
assert decaler("a", 3) == "d"
assert decaler("z", 3) == "c"
assert decaler("m", 0) == "m"
assert decaler("b", 26) == "b"
def cesar(txt: str, n: int) -> str:
"""Renvoie txt où chaque lettre de ALPHABET est décalée de n rangs,
les autres caractères restant inchangés."""
acc = ""
for car in txt:
if car in ALPHABET:
acc = acc + decaler(car, n)
else:
acc = acc + car
return acc
def test_cesar():
assert cesar("ave cesar, morituri te salutant", 3) == "dyh fhvdu, prulwxul wh vdoxwdqw"
assert cesar("zebre", 3) == "cheuh"
assert cesar("", 3) == ""
assert cesar(cesar("bonjour le monde", 3), 23) == "bonjour le monde"
cesar(message, 23) : décaler encore de 23, c'est faire 3 + 23 = 26 rangs en tout, soit un tour complet de l'alphabet. Le dernier test le vérifie.
Problème : ROT13
ROT13 est le chiffrement de César avec un décalage de 13. Il a une propriété étonnante : chiffrer deux fois redonne le message de départ.
- Écris
rot13(txt)en une seule ligne, en utilisantcesar. - Complète
test_rot13avec unassertqui vérifie cette propriété sur un texte de ton choix. Pourquoi est-elle vraie ?
def rot13(txt: str) -> str:
"""Renvoie txt chiffré par César avec un décalage de 13."""
...
def test_rot13():
assert rot13("bonjour") == "obawbhe"
...
Correction
def rot13(txt: str) -> str:
"""Renvoie txt chiffré par César avec un décalage de 13."""
return cesar(txt, 13)
def test_rot13():
assert rot13("bonjour") == "obawbhe"
assert rot13(rot13("ave cesar, morituri te salutant")) == "ave cesar, morituri te salutant"
13 font 26 rangs, soit un tour complet de l'alphabet : chaque lettre revient à sa place. On ne teste pas seulement un résultat, on teste une propriété.
Problème : palindrome
Un palindrome se lit de la même façon dans les deux sens : "kayak", "ressasser".
- Écris
est_palindrome(mot), en utilisantrenverser. - La phrase
"esope reste ici et se repose"est un palindrome si on ne tient pas compte des espaces. Quel appel le vérifie, sans rien changer àest_palindrome?
def est_palindrome(mot: str) -> bool:
"""Renvoie True si mot se lit de la même façon dans les deux sens."""
...
def test_est_palindrome():
assert est_palindrome("kayak")
assert not est_palindrome("chat")
...
Indice léger
Un mot est un palindrome quand il est égal à son renversement.
Correction
def est_palindrome(mot: str) -> bool:
"""Renvoie True si mot se lit de la même façon dans les deux sens."""
return mot == renverser(mot)
def test_est_palindrome():
assert est_palindrome("kayak")
assert not est_palindrome("chat")
assert est_palindrome("")
assert est_palindrome(sans_espaces("esope reste ici et se repose"))
est_palindrome(sans_espaces("esope reste ici et se repose")) : on enlève d'abord les espaces, puis on teste. Deux fonctions déjà écrites, combinées, sans en réécrire aucune.
Problème : le brin complémentaire d'ADN
Un brin d'ADN s'écrit avec quatre lettres, A, C, G et T. Le brin qui lui fait face s'obtient en remplaçant chaque A par un T, chaque T par un A, chaque C par un G et chaque G par un C.
Écris complementaire(brin).
def complementaire(brin: str) -> str:
"""Renvoie le brin complémentaire de brin.
Précondition : brin ne contient que les lettres A, C, G et T."""
...
def test_complementaire():
assert complementaire("ATTGC") == "TAACG"
...
Indice léger
C'est « Remplacer un caractère », mais avec quatre cas au lieu de deux : if, puis des elif.
Correction
def complementaire(brin: str) -> str:
"""Renvoie le brin complémentaire de brin.
Précondition : brin ne contient que les lettres A, C, G et T."""
acc = ""
for base in brin:
if base == "A":
acc = acc + "T"
elif base == "T":
acc = acc + "A"
elif base == "C":
acc = acc + "G"
else:
acc = acc + "C"
return acc
def test_complementaire():
assert complementaire("ATTGC") == "TAACG"
assert complementaire("") == ""
assert complementaire(complementaire("GATTACA")) == "GATTACA"
Problème : écrire en leet
En leet, on remplace certaines lettres par des chiffres qui leur ressemblent : a par 4, e par 3, i par 1 et o par 0. Ainsi "elite" devient "3l1t3".
On s'en sert pour fabriquer un mot de passe à partir d'un mot ou d'une phrase facile à retenir : le résultat contient des chiffres, ce que beaucoup de sites exigent. Mais les logiciels qui cherchent à casser les mots de passe essaient justement ces remplacements, qui n'ajoutent donc presque rien. Ce qui rend un mot de passe vraiment robuste, c'est sa longueur : une phrase entière vaut mieux qu'un mot déguisé.
Écris leet(txt).
def leet(txt: str) -> str:
"""Renvoie txt où a, e, i et o sont remplacés par 4, 3, 1 et 0."""
...
def test_leet():
assert leet("elite") == "3l1t3"
...
Correction
def leet(txt: str) -> str:
"""Renvoie txt où a, e, i et o sont remplacés par 4, 3, 1 et 0."""
acc = ""
for car in txt:
if car == "a":
acc = acc + "4"
elif car == "e":
acc = acc + "3"
elif car == "i":
acc = acc + "1"
elif car == "o":
acc = acc + "0"
else:
acc = acc + car
return acc
def test_leet():
assert leet("elite") == "3l1t3"
assert leet("bonjour") == "b0nj0ur"
assert leet("") == ""
Problème : parler javanais
Le javanais est un argot qui insère "av" dans les mots. Ici, on prend une règle simple : on place "av" devant chaque voyelle. Ainsi "chat" devient "chavat".
Écris javanais(mot).
def javanais(mot: str) -> str:
"""Renvoie mot où "av" est inséré devant chaque voyelle."""
...
def test_javanais():
assert javanais("chat") == "chavat"
...
Indice léger
Quand le caractère est une voyelle, on n'ajoute pas un morceau à acc, on en ajoute deux.
Correction
def javanais(mot: str) -> str:
"""Renvoie mot où "av" est inséré devant chaque voyelle."""
acc = ""
for car in mot:
if car in "aeiouy":
acc = acc + "av" + car
else:
acc = acc + car
return acc
def test_javanais():
assert javanais("chat") == "chavat"
assert javanais("python") == "pavythavon"
assert javanais("") == ""
Problème : un mot de passe robuste
On décide qu'un mot de passe est robuste s'il a au moins 8 caractères, et contient au moins une majuscule, et au moins un chiffre.
- Écris
contient_majuscule(mdp)etcontient_chiffre(mdp). - Écris
est_robuste(mdp), en utilisant les deux fonctions précédentes.
Rappels : car.isupper() dit si car est une majuscule, car.isdigit() s'il est un chiffre.
def contient_majuscule(mdp: str) -> bool:
"""Renvoie True si mdp contient au moins une majuscule."""
...
def contient_chiffre(mdp: str) -> bool:
"""Renvoie True si mdp contient au moins un chiffre."""
...
def est_robuste(mdp: str) -> bool:
"""Renvoie True si mdp a au moins 8 caractères, une majuscule et un chiffre."""
...
def test_est_robuste():
assert est_robuste("Tortue2026")
assert not est_robuste("tortue2026")
...
Indice léger
L'accumulateur est un booléen. Avant la première itération de la boucle, a-t-on trouvé une majuscule ? Que faut-il faire de lui quand on en rencontre une ?
Indice plus précis
trouve = False avant la boucle ; dans la boucle, if car.isupper(): trouve = True ; on renvoie trouve après. est_robuste n'a pas de boucle : elle combine trois conditions avec and.
Avant d'ouvrir la correction
Écris une phrase sur ton cahier : qu'est-ce que l'indice t'a appris sur ce qui bloquait dans ton code ?
Correction
def contient_majuscule(mdp: str) -> bool:
"""Renvoie True si mdp contient au moins une majuscule."""
trouve = False
for car in mdp:
if car.isupper():
trouve = True
return trouve
def contient_chiffre(mdp: str) -> bool:
"""Renvoie True si mdp contient au moins un chiffre."""
trouve = False
for car in mdp:
if car.isdigit():
trouve = True
return trouve
def est_robuste(mdp: str) -> bool:
"""Renvoie True si mdp a au moins 8 caractères, une majuscule et un chiffre."""
return len(mdp) >= 8 and contient_majuscule(mdp) and contient_chiffre(mdp)
def test_est_robuste():
assert est_robuste("Tortue2026")
assert not est_robuste("tortue2026")
assert not est_robuste("TortueNinja")
assert not est_robuste("T2")
trouve à False dans la boucle : une fois une majuscule trouvée, elle l'est pour de bon. Et est_robuste ne refait aucun parcours : elle se contente de combiner des fonctions déjà écrites.
Le même for, sur tout ce qui s'itère
Le for ne change pas selon ce qu'il parcourt. Tu n'as parcouru ici que des chaînes de caractères, mais quand tu rencontreras les listes, les tuples et les dictionnaires, ce sera EXACTEMENT ce for, tu n'auras rien de nouveau à apprendre.
La méthode sera exactement la même.