Vue d'ensemble
En prépa, la moitié du travail en informatique consiste à lire du code, pas à en écrire. En DS comme aux concours, on te demande sans arrêt de prévoir la sortie d'un programme, de dire ce qu'il calcule, ou de trouver l'erreur dans un code qui ne marche pas. Ce sont trois compétences distinctes de l'écriture — et elles se travaillent.
Cette fiche te donne la méthode : tracer un programme à la main (la table de trace),
reconnaître les patrons classiques en lecture (max, min, somme, comptage, recherche),
tester proprement avec assert et des jeux de cas bien choisis, et
déboguer en lisant le message d'erreur. C'est la boîte à outils du lecteur de code.
Prérequis
- Variables, affectation, types de base (int, float, str, list)
- Boucles
foretwhile, conditions - Fonctions : paramètres,
return - Listes et parcours par élément ou par indice
Tu sais coder mais tu bloques dès qu'il faut lire le code d'un autre ? Prévoir la sortie et repérer un bug sont des compétences à part entière, très évaluées et rarement enseignées. Nos mentors alumni X · Centrale · Mines t'entraînent à dérouler et déboguer n'importe quel programme sans machine.
Trouver un mentor →1. La table de trace, méthode systématique
Pour prévoir la sortie d'un programme, on ne devine pas : on le déroule, ligne par ligne, en notant la valeur de chaque variable dans un tableau. C'est la table de trace, l'outil numéro un du lecteur de code.
Un tableau où l'on suit l'exécution d'un programme : une colonne par variable (plus éventuellement une colonne pour la condition testée), et une ligne par tour de boucle. On remplit case par case en jouant le rôle de l'ordinateur.
- Note l'état initial des variables (avant la boucle) sur une première ligne.
- Pour chaque tour de boucle, crée une nouvelle ligne.
- Évalue les conditions avec les valeurs actuelles, puis applique les affectations.
- Ne modifie une variable qu'au moment où le code le fait — jamais avant.
- La dernière ligne donne les valeurs finales : c'est ce que le programme affiche ou renvoie.
Traçons un programme qui cherche le plus grand élément et son indice :
t = [4, 2, 9, 5]
m = t[0]
imax = 0
for i in range(1, len(t)):
if t[i] > m:
m = t[i]
imax = i
print(m, imax)| Tour | i | t[i] | t[i] > m ? | m | imax |
|---|---|---|---|---|---|
| init | — | — | — | 4 | 0 |
| 1 | 1 | 2 | faux | 4 | 0 |
| 2 | 2 | 9 | vrai | 9 | 2 |
| 3 | 3 | 5 | faux | 9 | 2 |
Le programme affiche 9 2 : le maximum est 9, situé à l'indice 2. Remarque que
range(1, len(t)) commence à 1 : on ne recompare pas le premier élément avec lui-même.
if
(ou du while) évite 90 % des erreurs de trace : on écrit noir sur blanc si on entre dans le
bloc ou non, au lieu de le décider de tête.
m
avant de tester t[i] > m, ta trace est fausse dès le premier tour.
2. Reconnaître les patrons en lecture
Un examinateur te montre un bout de code et demande : « que calcule cette fonction ? ». Plutôt que de tracer intégralement, on gagne un temps fou en reconnaissant le patron : une variable accumulée dans une boucle trahit presque toujours un max, un min, une somme, un comptage ou une recherche. Voici comment les repérer. (Pour écrire ces algorithmes toi-même, va voir la fiche dédiée « Patrons d'algorithmes classiques sur les listes ».)
Que calcule cette fonction ? Lis-la avant de dérouler la réponse.
def f(t):
r = t[0]
for x in t:
if x < r:
r = x
return rr = t[0]Initialisation par un vrai élément. On ne part pas de 0 : signe d'un patron max ou min.if x < r: r = xOn garde le plus petit vu. Le comparateur est <, donc r descend vers les petites valeurs.return rVerdict. C'est le minimum de la liste. Avec >, c'eût été le maximum.Deuxième extrait :
def g(t):
s = 0
for x in t:
s = s + x
return s / len(t)s = 0Accumulateur à zéro. On additionne : patron somme.s = s + xAjout de chaque élément. À la fin, s est la somme totale.return s / len(t)La division change tout. Somme divisée par l'effectif = moyenne.Troisième extrait — attention au repère du comptage :
def h(t):
c = 0
for x in t:
if x % 2 == 0:
c = c + 1
return cc = 0Compteur à zéro. On part de 0 comme la somme, mais…if x % 2 == 0: c = c + 1… on ajoute 1, pas x. C'est la signature du comptage : on n'ajoute que 1, et seulement sous condition.return cVerdict. Nombre d'éléments pairs de la liste.Dernier extrait — un retour anticipé dans la boucle :
def cherche(t, v):
for i in range(len(t)):
if t[i] == v:
return i
return -1if t[i] == v: return iSortie dès qu'on trouve. Le return dans la boucle stoppe la recherche au premier v rencontré : patron recherche séquentielle.return -1Le cas « absent ». On n'atteint cette ligne que si la boucle finit sans avoir trouvé : on renvoie l'indice-sentinelle -1.t[0] → max/min) et le cœur de la boucle (s + x → somme ;
c + 1 → comptage ; comparaison → max/min ; return anticipé → recherche). En
deux secondes, tu sais à quoi tu as affaire.
3. Tester : assert et jeux de tests
Un programme « qui a l'air juste » n'est pas un programme juste. On le teste : on lui
donne des entrées dont on connaît la réponse, et on vérifie qu'il la produit. L'outil de base est
l'instruction assert.
assert condition ne fait rien si la condition est vraie, et
interrompt le programme avec une AssertionError si elle est fausse. C'est
une alarme silencieuse tant que tout va bien.
def somme(t):
s = 0
for x in t:
s = s + x
return s
assert somme([1, 2, 3]) == 6 # cas normal
assert somme([5]) == 5 # cas limite : un seul élément
assert somme([]) == 0 # cas limite : liste vide
print("Tous les tests passent")assert somme([1, 2, 3]) == 6Cas normal. Une entrée « typique » dont on calcule la réponse à la main : 1+2+3 = 6.assert somme([5]) == 5Cas limite : un seul élément. La boucle ne tourne qu'une fois — un grand classique de bug.assert somme([]) == 0Cas limite : liste vide. La boucle ne tourne pas du tout ; somme doit renvoyer 0. Un algorithme mal écrit planterait ici.print("Tous les tests passent")Signal de vie. Si ce message s'affiche, aucun assert n'a échoué.- Le cas normal : une entrée représentative dont tu connais la réponse exacte.
- Le cas limite « petit » : liste à un seul élément, où la boucle tourne une fois.
- Le cas limite « vide » : liste vide, où la boucle ne tourne pas.
- Le cas piège : valeurs négatives, doublons, élément absent selon l'algorithme.
maximum([]) avec m = t[0]
lève une IndexError : la liste vide n'a pas d'élément d'indice 0. Selon le sujet, soit on
l'interdit (préconditions), soit on le traite à part. Tester la liste vide révèle immédiatement ce genre
de faille.
4. Déboguer : trouver l'erreur
Quand Python affiche du rouge, ne panique pas : le message te dit quel type d'erreur et à quelle ligne. Apprendre à le lire, c'est déjà régler la moitié des bugs.
4.1 Lire le message d'erreur
Trois erreurs reviennent tout le temps. Il faut les reconnaître d'un coup d'œil.
print(resultat)
# NameError: name 'resultat' is not definedresultat vs resutlat), ou définie après son utilisation.
t = [1, 2, 3]
print(t[3])
# IndexError: list index out of ranget[3] déborde. Cause typique : range(len(t) + 1) au lieu de
range(len(t)).
print("3" + 3)
# TypeError: can only concatenate str (not "int") to strinput() (qui renvoie une chaîne) qu'on a oublié de convertir avec int(...).
4.2 Déboguer avec print
Le code ne plante pas mais donne un mauvais résultat ? Insère des print pour
voir les valeurs pendant l'exécution, au lieu de les imaginer.
def somme_positifs(t):
s = 0
for x in t:
if x > 0:
s = s + x
print("x =", x, "| s =", s) # espion temporaire
return s
somme_positifs([3, -1, 4])x = 3 | s = 3, puis
x = -1 | s = 3 (le négatif est bien ignoré), puis x = 4 | s = 7. On
voit l'accumulation se faire, tour après tour. Une fois le bug compris, on retire les
print espions.
4.3 La dichotomie sur le code
Sur un programme long, on ne relit pas tout : on coupe en deux. On place un
print au milieu pour vérifier que les valeurs y sont encore correctes. Si oui, le bug est
dans la seconde moitié ; sinon, dans la première. On recommence sur la moitié fautive. En quelques
coupes, on encercle la ligne coupable — la même idée que la recherche par dichotomie.
Le débogage, ça se transmet. Savoir lire une IndexError et remonter à la
ligne fautive en 30 secondes, ça change une note de DS. Nos mentors alumni X · Centrale · Mines
t'apprennent leurs réflexes de lecture et de test, sur tes propres codes.
5. Lire une fonction qu'on n'a pas écrite
Face à une fonction inconnue, on ne lit pas au hasard : on suit une checklist qui donne du sens au code en moins d'une minute.
- Le nom : souvent un indice (mais ne pas s'y fier aveuglément).
- Les entrées : combien de paramètres, de quels types (liste ? entier ?).
- La sortie : que renvoie le
return— un nombre, une liste, un booléen ? - La trace : déroule la fonction sur un petit exemple concret (2 ou 3 valeurs).
Applique la méthode à cette fonction au nom volontairement muet :
def mystere(t):
r = []
for x in t:
if x not in r:
r.append(x)
return r
Entrées : une liste t. Sortie : une nouvelle liste
r (elle démarre vide et on lui ajoute des éléments). Traçons sur
[1, 2, 2, 3, 1] :
| Tour | x | x déjà dans r ? | r après |
|---|---|---|---|
| init | — | — | [] |
| 1 | 1 | non | [1] |
| 2 | 2 | non | [1, 2] |
| 3 | 2 | oui | [1, 2] |
| 4 | 3 | non | [1, 2, 3] |
| 5 | 1 | oui | [1, 2, 3] |
Verdict : mystere supprime les doublons en conservant l'ordre de première
apparition. Le test x not in r n'ajoute un élément que s'il est nouveau. Le petit exemple a
tout révélé — bien plus vite qu'en fixant le code.
1 et les deux 2)
pour voir le comportement clé. Une liste sans répétition n'aurait rien montré.
6. Exercices d'application
À faire de tête, table de trace à l'appui, avant d'ouvrir le corrigé.
Trace ce programme et donne ce qu'il affiche.
t = [5, 1, 8, 3]
m = t[0]
for x in t:
if x < m:
m = x
print(m)Voir la correction détaillée
m = 5 (le premier élément).m = 1. x=8 : 8 < 1 non. x=3 : 3 < 1 non.Trace q sur [3, -1, 4, 0], puis dis en une phrase ce qu'elle calcule.
def q(t):
c = 0
for x in t:
if x > 0:
c = c + 1
return cVoir la correction détaillée
q([3, -1, 4, 0]) renvoie 2.c = c + 1 sous condition : c'est un comptage. q compte les éléments strictement positifs de la liste.7. Erreurs classiques (les pièges qui coûtent des points)
Ces erreurs ne viennent pas d'un manque de connaissances mais d'un manque de méthode. Les connaître, c'est déjà les éviter.
[3, 7, 2]
peut lever une IndexError sur [] (accès à t[0]) ou une
ZeroDivisionError sur une moyenne (division par len(t) == 0). Teste toujours la
liste vide et la liste à un élément.
NameError (nom inexistant) n'a rien à voir
avec un IndexError (indice hors bornes) ni un TypeError (types incompatibles).
Lire le type de l'erreur oriente directement vers la cause ; le confondre, c'est chercher au
mauvais endroit.
print(m) au lieu de
return m affiche la valeur mais renvoie None. Du coup
assert maximum([3, 7]) == 7 échoue, et x = maximum([3, 7]) met None
dans x. Pour tester une fonction, il lui faut un vrai return.
tri ne trie pas
forcément, et mystere peut faire quelque chose de simple. Le nom oriente, le code décide :
trace toujours pour confirmer.
7. Pour aller plus loin
Lire, tracer et tester sont des réflexes transversaux : tu les réinvestis dans presque tous les chapitres d'informatique. Voici les suites naturelles.
- Patrons d'algorithmes classiques sur les listes — pour écrire toi-même les algos max, min, somme, comptage et recherche que cette fiche apprend à reconnaître.
- Recherche par dichotomie — l'algorithme phare pour lequel la table de trace est indispensable, et où le débogage des bornes est un art.
- Correction et terminaison d'un algorithme — pour prouver rigoureusement qu'un programme fait ce qu'on croit et s'arrête, au-delà des simples tests.
Récap final — Ce qu'il faut absolument retenir
Tu dois pouvoir répondre « oui » sans hésiter à chaque point.
- Sais-tu dresser une table de trace avec une colonne par variable et une ligne par tour de boucle ?
- Sais-tu qu'on lit les anciennes valeurs pour tester les conditions, puis qu'on écrit les nouvelles ?
- Sais-tu reconnaître en lecture les patrons max, min, somme, comptage et recherche ?
- Sais-tu que l'initialisation (0 ou
t[0]) et le cœur de boucle trahissent le patron ? - Sais-tu distinguer un comptage (on ajoute 1) d'une somme (on ajoute
x) ? - Sais-tu écrire des tests avec
assertet ce que faitassertquand la condition est fausse ? - Sais-tu choisir un jeu de tests : cas normal, un seul élément, liste vide, cas piège ?
- Sais-tu reconnaître un
NameError, unIndexErroret unTypeError? - Sais-tu déboguer en insérant des
printespions, puis en les retirant ? - Sais-tu appliquer la dichotomie sur le code pour encercler la ligne fautive ?
- Sais-tu comprendre une fonction inconnue (nom, entrées, sortie, trace sur un petit exemple) ?
- Sais-tu qu'une fonction qui
printau lieu dereturnrenvoieNone?