Mise au point des programmes
Introduction
Un programme marche rarement du premier coup. Le travail de mise au point — détecter les erreurs, comprendre leur origine, les corriger, vérifier que le programme fait bien ce qu'il doit faire — occupe souvent plus de temps que l'écriture du code lui-même. Ce n'est pas un échec : c'est le cœur du métier.
Un bon programmeur ressemble moins à un magicien qu'à un détective : il collecte des indices (messages d'erreur, valeurs intermédiaires), formule des hypothèses, les confronte aux faits, et resserre son enquête jusqu'au coupable. L'enjeu de ce cours est de transformer la mise au point en démarche méthodique plutôt qu'en errance hasardeuse devant un message d'erreur incompréhensible.
Les trois grands types d'erreurs
| Type | Symptôme | Exemple |
|---|---|---|
| Syntaxe | Python refuse même de lancer le programme. | Oubli des :, parenthèse non fermée. |
| Exécution | Le programme démarre puis plante avec une exception. | Division par zéro, indice hors liste. |
| Logique | Le programme tourne, mais le résultat est faux. | Le calcul de moyenne renvoie un mauvais nombre. |
Les erreurs logiques sont les plus dangereuses : sans test, elles peuvent passer inaperçues.
Erreur de syntaxe
if x > 0
print("positif")
Sortie :
File "...", line 1
if x > 0
^
SyntaxError: expected ':'
Le : manquant suffit à empêcher l'exécution.
Erreur d'exécution (exception)
notes = [12, 15, 14]
print(notes[10])
Sortie :
IndexError: list index out of range
Python a tenté l'opération et a abandonné en chemin.
Erreur de logique
def moyenne(notes):
total = 0
for n in notes:
total + n # oubli du = → ne modifie pas total
return total / len(notes)
print(moyenne([10, 12, 14])) # 0.0 — pas l'erreur attendue !
Le programme tourne sans crier, mais répond n'importe quoi.
Quel est le type d'erreur le plus difficile à détecter ?
Lire un message d'erreur
Quand Python plante, il affiche une trace (traceback) qui se lit de haut en bas pour suivre l'enchaînement et de bas en haut pour trouver la cause immédiate.
Traceback (most recent call last):
File "calculs.py", line 12, in <module>
print(moyenne([]))
File "calculs.py", line 8, in moyenne
return total / len(notes)
ZeroDivisionError: division by zero
Trois informations clés :
- Le type d'erreur :
ZeroDivisionError. - Le message :
division by zero. - L'endroit : ligne 8 dans la fonction
moyenne.
Ici, l'enchaînement révèle qu'on appelle moyenne([]) — liste vide,
len([]) == 0, division par zéro. La cause est dans l'appel, pas dans
la fonction.
Traceback.Le print de débogage
L'outil de débogage le plus universel : afficher l'état des variables à des points clés du programme.
def moyenne(notes):
total = 0
for n in notes:
print("avant :", total, "ajout :", n) # debug
total += n
print("après :", total) # debug
return total / len(notes)
print(moyenne([10, 12, 14]))
Sortie :
avant : 0 ajout : 10
après : 10
avant : 10 ajout : 12
après : 22
avant : 22 ajout : 14
après : 36
12.0
On voit en direct comment total évolue, et on confirme que la valeur
finale est cohérente. Une fois le bug corrigé, on retire les print.
Jeux de tests
Pour qu'un programme soit correct, il ne suffit pas qu'il marche sur un exemple : il faut qu'il marche sur un échantillon représentatif d'entrées possibles. C'est un jeu de tests.
Quatre familles de cas à toujours envisager :
| Famille | Exemples pour moyenne(notes) |
|---|---|
| Cas normaux | [10, 12, 14], [8, 15] |
| Cas limites | [20, 20, 20] (toutes à 20), [0] (un seul élément) |
| Cas extrêmes | liste de 100 notes, notes très proches de 0 ou 20 |
| Cas interdits | [] (vide), [25] (hors plage) → précondition |
Tester avec assert
Le moyen le plus simple de coder un jeu de tests : enchaîner des assertions qui doivent toutes passer.
def double(n):
return n * 2
# Jeu de tests
assert double(0) == 0
assert double(3) == 6
assert double(-2) == -4
assert double(100) == 200
print("Tous les tests passent.")
Si une assertion échoue, Python lève AssertionError à la ligne précise.
Si toutes passent, on voit le message final.
Sortie attendue :
Tous les tests passent.
Démarche méthodique
Devant un bug, suivre la séquence :
- Reproduire le bug — quel appel précis le déclenche ?
- Isoler : trouver l'entrée la plus simple qui plante.
- Lire le message d'erreur si présent.
- Tracer :
printou débogueur pour suivre les valeurs. - Formuler une hypothèse sur la cause.
- Corriger, puis relancer le jeu de tests complet — pas seulement le cas qui plantait.
La dernière étape est cruciale : une correction peut introduire un nouveau bug. C'est la régression. Le jeu de tests complet protège contre cela.
Que se passe-t-il si assert 1 == 2 est exécuté ?
Pièges courants
- Modifier au hasard : changer une ligne en espérant que ça marche sans comprendre est la pire stratégie.
- Tester uniquement les cas normaux : un programme correct gère aussi les cas limites (liste vide, valeur nulle…).
- Oublier de relancer tous les tests après correction — risque de régression.
- Confondre absence d'erreur et correction : un programme qui ne plante pas peut tout à fait donner de mauvais résultats.
- Laisser les
printde debug dans le code final.
Pour aller plus loin
Le jeu de tests est tellement central qu'il a donné naissance à un style de programmation entier : le TDD (Test-Driven Development) — on écrit les tests avant le code. Vous le pratiquerez avec naturel dès que la spécification sera bien en place : précondition → postcondition → tests → code.