Partie 4 : Mise au point des programmes et gestion des bugs¶
Programme officiel (B.O.)¶
B.O. spécial n° 8 du 25 juillet 2019 - NSI Terminale
| Contenus | Capacités attendues | Commentaires |
|---|---|---|
| Mise au point des programmes. Gestion des bugs. | Dans la pratique de la programmation, savoir répondre aux causes typiques de bugs : problèmes liés au typage, effets de bord non désirés, débordements dans les tableaux, instruction conditionnelle non exhaustive, choix des inégalités, comparaisons et calculs entre flottants, mauvais nommage des variables, etc. | On prolonge le travail entrepris en classe de première sur l'utilisation de la spécification, des assertions, de la documentation des programmes et de la construction de jeux de tests. Les élèves apprennent progressivement à anticiper leurs erreurs. |
1. Les types de bugs¶
1.1 Erreurs de syntaxe¶
Détectées avant l'exécution par l'interpréteur Python. Le programme ne démarre pas.
# Parenthèse manquante
print("bonjour"
# SyntaxError: unexpected EOF while parsing
# Indentation incorrecte
def f():
print("test")
# IndentationError: expected an indented block
1.2 Erreurs d'exécution (exceptions)¶
Détectées pendant l'exécution. Le programme s'arrête brutalement.
# Division par zéro
print(10 / 0)
# ZeroDivisionError
# Index hors limites
tab = [1, 2, 3]
print(tab[5])
# IndexError: list index out of range
# Type incompatible
print("age : " + 17)
# TypeError: can only concatenate str to str
1.3 Erreurs logiques (sémantiques)¶
Les plus difficiles à détecter : le programme s'exécute sans erreur mais produit un résultat faux.
# Bug : calcul de la moyenne
def moyenne(notes):
return sum(notes) / len(notes) + 1 # le + 1 est une erreur
2. Causes typiques de bugs¶
2.1 Problèmes liés au typage¶
# Concaténation au lieu d'addition
age = input("Âge : ") # renvoie une chaîne
print(age + 1) # TypeError !
# Correction : int(input("Âge : "))
# Confusion entier / flottant
print(7 / 2) # 3.5 (division réelle)
print(7 // 2) # 3 (division entière)
2.2 Effets de bord non désirés¶
Un effet de bord se produit quand une fonction modifie quelque chose en dehors d'elle (variable globale, argument mutable).
# Bug classique : modification d'une liste passée en argument
def ajouter_element(liste, element):
liste.append(element) # modifie la liste originale !
return liste
ma_liste = [1, 2, 3]
nouvelle = ajouter_element(ma_liste, 4)
print(ma_liste) # [1, 2, 3, 4] - modifiée involontairement !
# Correction : travailler sur une copie
def ajouter_element(liste, element):
copie = liste.copy()
copie.append(element)
return copie
# Bug classique : argument par défaut mutable
def ajouter(element, liste=[]): # DANGER !
liste.append(element)
return liste
print(ajouter(1)) # [1]
print(ajouter(2)) # [1, 2] - surprise !
# Correction : utiliser None comme valeur par défaut
def ajouter(element, liste=None):
if liste is None:
liste = []
liste.append(element)
return liste
2.3 Débordements dans les tableaux¶
tab = [10, 20, 30]
# Bug : index hors limites
for i in range(len(tab) + 1): # range(4) → 0, 1, 2, 3
print(tab[i]) # IndexError quand i = 3
# Correction
for i in range(len(tab)): # range(3) → 0, 1, 2
print(tab[i])
2.4 Instruction conditionnelle non exhaustive¶
# Bug : cas non traité
def categorie(age):
if age < 12:
return "enfant"
elif age < 18:
return "adolescent"
# Que se passe-t-il si age >= 18 ? → renvoie None
# Correction
def categorie(age):
if age < 12:
return "enfant"
elif age < 18:
return "adolescent"
else:
return "adulte"
2.5 Choix des inégalités (erreur « off-by-one »)¶
# Bug : < au lieu de <=
def est_dans_intervalle(x, a, b):
return a < x < b # exclut a et b
# Si on veut inclure les bornes :
def est_dans_intervalle(x, a, b):
return a <= x <= b
# Bug : boucle qui itère une fois de trop ou de moins
for i in range(1, 10): # 1, 2, ..., 9 (pas 10 !)
for i in range(1, 11): # 1, 2, ..., 10
2.6 Comparaisons entre flottants¶
# Bug : comparaison directe de flottants
print(0.1 + 0.2 == 0.3) # False !
print(0.1 + 0.2) # 0.30000000000000004
# Correction : comparer avec une tolérance
def presque_egal(a, b, epsilon=1e-9):
return abs(a - b) < epsilon
print(presque_egal(0.1 + 0.2, 0.3)) # True
2.7 Mauvais nommage des variables¶
# Bug : confusion entre variables aux noms proches
longueur = 10
largeur = 5
aire = longueur * longueur # devrait être longueur * largeur
# Bug : variable locale masquant une variable de portée supérieure
total = 100
def ajouter(n):
total = 0 # crée une variable locale, ne modifie pas la globale
total += n
return total
3. Techniques de débogage¶
3.1 Affichage avec print¶
La méthode la plus simple : insérer des print() pour observer les valeurs des variables.
def recherche(tab, val):
for i in range(len(tab)):
print(f" i={i}, tab[i]={tab[i]}, val={val}") # debug
if tab[i] == val:
return i
return -1
3.2 Assertions¶
Les assertions vérifient des conditions qui doivent être vraies. Si la condition est fausse, le programme s'arrête avec un message explicite.
def moyenne(notes):
assert len(notes) > 0, "La liste de notes ne doit pas être vide"
assert all(0 <= n <= 20 for n in notes), "Notes entre 0 et 20"
return sum(notes) / len(notes)
3.3 Documentation et spécification¶
def recherche_dicho(tab, val):
"""Recherche val dans tab (trié) par dichotomie.
Args:
tab: liste triée par ordre croissant
val: valeur à rechercher
Returns:
L'indice de val si trouvé, -1 sinon.
Raises:
AssertionError: si tab n'est pas trié.
"""
assert all(tab[i] <= tab[i+1] for i in range(len(tab)-1)), "tab doit être trié"
# ...
3.4 Jeux de tests¶
Tester systématiquement avec des cas variés :
def est_premier(n):
if n < 2:
return False
for i in range(2, int(n ** 0.5) + 1):
if n % 2 == 0:
return False
return True
# Jeu de tests
assert est_premier(2) == True
assert est_premier(3) == True
assert est_premier(4) == False
assert est_premier(17) == True
assert est_premier(1) == False
assert est_premier(0) == False
assert est_premier(-5) == False
# Bug détecté ! est_premier(9) renvoie True au lieu de False
# Cause : n % 2 au lieu de n % i dans la boucle
3.5 Le débogueur¶
Un débogueur (Thonny, VS Code, etc.) permet :
- l'exécution pas à pas ;
- l'inspection des variables à chaque étape ;
- la pose de points d'arrêt (breakpoints) ;
- la navigation dans la pile d'appels.
4. Bonnes pratiques pour éviter les bugs¶
| Pratique | Description |
|---|---|
| Nommer clairement | indice_max plutôt que x ou temp |
| Tester au fur et à mesure | Tester chaque fonction dès qu'elle est écrite |
| Écrire les tests d'abord | Définir les cas de test avant de coder (TDD) |
| Limiter la portée | Éviter les variables globales |
| Documenter | Docstrings pour chaque fonction |
| Utiliser des assertions | Vérifier les préconditions |
| Copier les mutables | Ne pas modifier involontairement les listes passées en argument |
| Éviter la comparaison de flottants | Utiliser une tolérance (epsilon) |
À retenir¶
| Type de bug | Exemple | Détection |
|---|---|---|
| Syntaxe | Parenthèse manquante | Avant l'exécution |
| Exécution | Division par zéro, index hors limites | Pendant l'exécution (exception) |
| Logique | Résultat faux, condition non exhaustive | Par les tests |
| Typage | "5" + 3 |
TypeError à l'exécution |
| Effet de bord | Modification involontaire d'une liste | Par les tests |
| Off-by-one | < au lieu de <=, range(n) vs range(n+1) |
Par les tests |
| Flottants | 0.1 + 0.2 != 0.3 |
Comparaison avec epsilon |