Discipline de Programmation et Programmation
Défensive
Spécification, Précondition, Postcondition & Assertions
Cours MP2I
Dr. Noulapeu Ngaffo Armielle
Objectifs du cours
À la fin de ce cours, l’apprenant sera capable de :
— Comprendre et rédiger des spécifications formelles de fonctions
— Utiliser les préconditions et postconditions pour documenter le contrat d’une fonction
— Implémenter la programmation défensive avec des assertions
— Déboguer efficacement en validant les invariants de programme
— Rendre des programmes plus robustes et maintenables
1
Discipline de Programmation et Programmation Défensive 2
1 Introduction : Pourquoi la discipline de programma-
tion ?
1.1 Le problème : Code fragile et difficile à maintenir
La plupart des bugs en production proviennent de :
— Hypothèses implicites non documentées
— États invalides non détectés
— Comportement inattendu en cas d’entrées invalides
— Manque de clarté sur ce qu’une fonction est censée faire
Exemple : Considérez cette fonction simple :
def division (a , b ) :
return a / b
Que se passe-t-il si b = 0 ? Et si a ou b ne sont pas des nombres ? La fonction ne précise
rien de ces cas particuliers.
1.2 La solution : Un contrat explicite entre le programmeur et l’uti-
lisateur
La discipline de programmation établit un contrat clair :
— La spécification décrit ce que la fonction doit faire
— Les préconditions énoncent ce que l’appelant doit garantir
— Les postconditions énoncent ce que la fonction garantit en retour
— Les assertions vérifient le respect du contrat à l’exécution
Analogie utile
Un contrat entre une entreprise de livraison et un client :
— Spécification : Livrer un colis en bon état
— Précondition (client) : Fournir une adresse valide et valider le contenu
— Postcondition (entreprise) : Colis livré à la date prévue sans dommage
— Assertion : Vérification du contenu à la livraison
MP2I
Discipline de Programmation et Programmation Défensive 3
2 Spécifications formelles
2.1 Qu’est-ce qu’une spécification ?
Une spécification est une description précise et non ambiguë de ce qu’une fonction doit
faire. Elle répond à la question : ”Quel est le comportement attendu ?”
2.2 Éléments d’une spécification complète
Une bonne spécification contient :
1. Signature : Les paramètres et le type de retour
2. Description : Ce que la fonction fait en langage naturel clair
3. Préconditions : Conditions que les entrées doivent satisfaire
4. Postconditions : Propriétés garanties sur la sortie
5. Cas particuliers : Comportement en cas d’erreur ou de données invalides
2.3 Exemple 1 : Spécification d’une fonction de recherche
Fonction : recherche(liste, element)
Description : Retourne l’indice de la première occurrence de element dans liste, ou
−1 si l’élément n’existe pas.
Préconditions :
— liste est une liste non vide
— element est du même type que les éléments de liste
Postconditions :
— Si le résultat est ≥ 0 : liste[résultat] == element
— Si le résultat est −1 : element ne figure pas dans liste
— Le résultat est le plus petit indice où element apparaı̂t
Complexité : O(n) où n est la longueur de la liste
2.4 Exemple 2 : Spécification d’une fonction de tri
Fonction : tri(liste)
Description : Trie la liste en place en ordre croissant.
Préconditions :
— liste est une liste de nombres
— Les éléments sont comparables (pas de None)
Postconditions :
— La liste est modifiée en place (aucune copie créée)
— Pour tout i < j : liste[i] <= liste[j]
— Les mêmes éléments sont présents (aucun élément ajouté/supprimé)
Complexité : O(n log n) en moyenne
MP2I
Discipline de Programmation et Programmation Défensive 4
3 Préconditions et Postconditions
3.1 Les Préconditions : Responsabilité de l’appelant
Une précondition est une condition que l’appelant doit garantir avant d’appeler la fonc-
tion. Si une précondition n’est pas respectée, le comportement est indéfini.
Philosophie :
— La fonction suppose que la précondition est vraie
— La fonction ne vérifie pas la précondition (c’est du travail inutile)
— C’est à l’appelant de s’assurer que les conditions sont remplies
3.2 Exemple de préconditions
def racine_carree ( x ) :
"""
Calcule la racine c a r r e de x .
P r c o n d i t i o n : x >= 0 ( x doit tre positif ou nul )
"""
return math . sqrt ( x )
# Utilisation correcte
resultat = racine_carree (9) # OK , 9 >= 0
print ( resultat ) # 3.0
# Utilisation incorrecte ( viole la p r c o n d i t i o n )
resultat = racine_carree ( -4) # ERREUR ! -4 < 0
3.3 Les Postconditions : Responsabilité du fournisseur
Une postcondition est une propriété que la fonction garantit après son exécution, si la
précondition était respectée.
Philosophie :
— La fonction promet que la postcondition sera vraie
— Si la précondition était vraie au départ, la postcondition sera vraie en sortie
— L’appelant peut faire confiance à cette garantie
3.4 Exemple de postconditions
def insertion_triee ( liste_triee , element ) :
"""
I n s r e element dans liste_triee au bon endroit .
P r c o n d i t i o n : liste_triee est t r i e en ordre croissant
Postconditions :
- La liste contient element
- La liste reste t r i e en ordre croissant
- Aucun autre lment n ’ est a j o u t ou s u p p r i m
"""
# Impl mentation
MP2I
Discipline de Programmation et Programmation Défensive 5
pass
# Utilisation
ma_liste = [1 , 3 , 5 , 7] # P r c o n d i t i o n : liste t r i e
insertion_triee ( ma_liste , 4)
print ( ma_liste ) # [1 , 3 , 4 , 5 , 7] - Postcondition : toujours t r i e !
3.5 Contrat : Précondition Postcondition
Le Contrat
Si la précondition est vraie avant l’appel
Alors la postcondition sera vraie après l’appel
Sinon le comportement est indéfini
3.6 Exemple complet : Fonction de division avec contrat
def division_entiere (a , b ) :
"""
Calcule le quotient de la division e n t i r e de a par b .
Pr conditions :
- a est un entier
- b est un entier
- b != 0 ( division par z r o interdite )
Postconditions :
- Retourne un entier q tel que a = b * q + r avec 0 <= r < b
- Le r s u l t a t est le quotient dans la division euclidienne
"""
return a // b
# Correct
print ( division_entiere (17 , 5) ) # 3 , car 17 = 5*3 + 2
# Viole la p r c o n d i t i o n : pas de v r i f i c a t i o n !
# print ( division_entiere (17 , 0) ) # ERREUR non g r e
MP2I
Discipline de Programmation et Programmation Défensive 6
4 Programmation défensive et Assertions
4.1 Qu’est-ce que la programmation défensive ?
La programmation défensive est une approche qui suppose que :
— Les erreurs arrivent (entrées invalides, états incohérents)
— Il est préférable de détecter et signaler les erreurs rapidement
— Mieux vaut échouer bruyamment que silencieusement
Principe : Fail fast, fail loud (échouer rapidement, échouer bruyamment)
4.2 Les Assertions : Vérification des contrats
Une assertion est une vérification au moment de l’exécution qui s’assure qu’une condition
est vraie. Si la condition est fausse, le programme s’arrête avec un message d’erreur.
Syntaxe Python :
assert condition , " Message d ’ erreur descriptif "
4.3 Types d’assertions
4.3.1 1. Assertions de préconditions
Vérifient que les conditions d’entrée sont valides :
def racine_c arr ee _s af e ( x ) :
" " " Calcule la racine c a r r e avec v r i f i c a t i o n . " " "
assert isinstance (x , ( int , float ) ) , " x doit tre un nombre "
assert x >= 0 , f " x doit tre positif , r e u : { x } "
return math . sqrt ( x )
# Appel valide
print ( racine _c ar re e_ sa fe (9) ) # 3.0
# Appels invalides - assertions chouent
# print ( raci ne _c ar re e_ sa fe (" hello ") ) # AssertionError
# print ( raci ne _c ar re e_ sa fe ( -5) ) # AssertionError
4.3.2 2. Assertions de postconditions
Vérifient que les garanties de sortie sont respectées :
def tri_safe ( liste ) :
" " " Trie une liste en place avec v r i f i c a t i o n . " " "
assert isinstance ( liste , list ) , " E n t r e doit tre une liste "
# I m p l m e n t a t i o n du tri
liste . sort ()
# V r i f i e r la postcondition : la liste est t r i e
for i in range ( len ( liste ) - 1) :
assert liste [ i ] <= liste [ i +1] , \
" Postcondition v i o l e : liste non t r i e "
return liste
MP2I
Discipline de Programmation et Programmation Défensive 7
# Utilisation
ma_liste = [3 , 1 , 4 , 1 , 5]
tri_safe ( ma_liste )
print ( ma_liste ) # [1 , 1 , 3 , 4 , 5]
4.3.3 3. Assertions d’invariants
Vérifient qu’une propriété reste vraie pendant l’exécution :
def echange r_elem ents ( liste , i , j):
" " " change deux lments avec v r i f i c a t i o n d ’ invariants . " " "
assert 0 <= i < len ( liste ) , " Index i invalide "
assert 0 <= j < len ( liste ) , " Index j invalide "
# Invariant : la somme des lments reste constante
somme_avant = sum ( liste )
liste [ i ] , liste [ j ] = liste [ j ] , liste [ i ]
# V r i f i e r l ’ invariant a p r s l ’ change
somme_apres = sum ( liste )
assert somme_avant == somme_apres , \
" Invariant v i o l : somme m o d i f i e "
4.4 Cas d’usage : Recherche avec contrat
def recherche_safe ( liste , cible ) :
"""
Recherche cible dans liste .
Pr conditions :
- liste est une liste non vide
- cible est du m m e type que les lments
Postconditions :
- Retourne l ’ indice de cible ou -1 si absent
- Le r s u l t a t est la p r e m i r e occurrence
"""
# V r i f i e r les p r c o n d i t i o n s
assert isinstance ( liste , list ) and len ( liste ) > 0 , \
" liste doit tre non vide "
assert cible is not None , \
" cible ne doit pas tre None "
# E x c u t e r la recherche
for i , element in enumerate ( liste ) :
if element == cible :
# V r i f i e r postcondition : liste [ r s u l t a t ] == cible
assert liste [ i ] == cible , \
" Postcondition v i o l e "
return i
MP2I
Discipline de Programmation et Programmation Défensive 8
return -1
# Utilisation correcte
resultat = recherche_safe ([1 , 2 , 3 , 4 , 5] , 3)
print ( resultat ) # 2
# Utilisation avec v r i f i c a t i o n
assert resultat == 2 , " Le r s u l t a t attendu tait 2"
MP2I
Discipline de Programmation et Programmation Défensive 9
4.5 Bonne pratique : Les trois niveaux de défense
Architecture défensive
1. Niveau 1 (Externe) : Validation à la frontière du programme
2. Niveau 2 (Interfaces) : Assertions au départ et à l’arrivée des fonctions
3. Niveau 3 (Interne) : Assertions sur les invariants internes
class Compte :
" " " Compte bancaire avec d f e n s e trois niveaux . " " "
def __init__ ( self , solde_initial =0) :
# Niveau 2 : v r i f i e r la p r c o n d i t i o n
assert solde_initial >= 0 , \
" Solde initial doit tre non n g a t i f "
self . solde = solde_initial
def deposer ( self , montant ) :
" " " D p o s e du montant sur le compte . " " "
# Niveau 2 : P r c o n d i t i o n s
assert isinstance ( montant , ( int , float ) ) , \
" Montant doit tre num rique"
assert montant > 0 , \
" Montant doit tre positif "
solde_avant = self . solde
self . solde += montant
# Niveau 3 : Invariant - solde augmente
assert self . solde > solde_avant , \
" Invariant : solde doit augmenter "
# Niveau 3 : Invariant - solde reste positif
assert self . solde >= 0 , \
" Invariant : solde ne doit pas tre n gatif"
def retirer ( self , montant ) :
" " " Retire du montant du compte . " " "
# Niveau 2 : P r c o n d i t i o n s
assert isinstance ( montant , ( int , float ) ) , \
" Montant doit tre num rique"
assert montant > 0 , \
" Montant doit tre positif "
assert self . solde >= montant , \
" Solde insuffisant "
solde_avant = self . solde
self . solde -= montant
# Niveau 3 : Invariant - solde diminue
assert self . solde < solde_avant , \
" Invariant : solde doit diminuer "
# Niveau 3 : Invariant - solde reste positif
assert self . solde >= 0 , \
MP2I
Discipline de Programmation et Programmation Défensive 10
" Invariant : solde ne doit pas tre n gatif"
# Niveau 1 : Validation des e n t r e s utilisateur
try :
montant_saisi = input ( " Montant d p o s e r : ")
montant = float ( montant_saisi )
assert montant > 0 , " Veuillez entrer un montant positif "
except ValueError :
print ( " Erreur : e n t r e invalide " )
MP2I
Discipline de Programmation et Programmation Défensive 11
5 Stratégies et Bonnes pratiques
5.1 Quand utiliser les assertions ?
Utilisez les assertions pour
— Vérifier les préconditions des fonctions
— Vérifier les postconditions avant de retourner
— Vérifier les invariants pendant l’exécution
— Détecter les erreurs de logique interne
— Valider les suppositions du code
N’utilisez PAS les assertions pour
— Valider les entrées utilisateur (utiliser try/except)
— Contrôler le flux d’exécution normal
— Signaler des erreurs attendues au runtime
— Remplacer la gestion d’erreurs appropriée
5.2 Exemple : Validation d’entrées utilisateur vs Assertions
# MAUVAIS : utiliser les assertions pour les e n t r e s
def mauvais_exemple () :
age = int ( input ( " Entrez votre ge : " ) )
assert age > 0 , " L ’ ge doit tre positif " # MAUVAIS
return age
# BON : utiliser try / except pour les e n t r e s
def bon_exemple () :
while True :
try :
age = int ( input ( " Entrez votre ge : " ) )
if age <= 0:
print ( " Erreur : l ’ ge doit tre positif " )
continue
return age
except ValueError :
print ( " Erreur : veuillez entrer un nombre entier " )
5.3 Messages d’assertion clairs et utiles
Un bon message d’assertion :
— Explique ce qui a échoué
— Explique pourquoi c’est un problème
— Fournit les valeurs problématiques si possible
# MAUVAIS : message peu informatif
assert len ( password ) > 8
# BON : message clair et utile
assert len ( password ) > 8 , \
f " Mot de passe trop court : { len ( password ) } chars , " \
MP2I
Discipline de Programmation et Programmation Défensive 12
f " minimum 8 requis "
# MAUVAIS
assert age >= 0
# BON
assert age >= 0 , \
f " ge invalide : { age }. Doit tre >= 0 "
5.4 Assertions en production
Important : Les assertions peuvent être désactivées en Python avec -O ou -OO.
# Pour les erreurs qui NE DOIVENT PAS tre d sactiv es :
if condition _c ri ti qu e :
raise ValueError ( " Erreur critique " )
# Pour les v r i f i c a t i o n s de d v e l o p p e m e n t :
assert condition_dev , " Erreur de d v e l o p p e m e n t "
MP2I
Discipline de Programmation et Programmation Défensive 13
6 Étude de cas complète : Implémentation d’une pile
6.1 Spécification d’une pile
Une pile (stack) est une structure de données LIFO (Last In, First Out).
Opérations :
— empiler(element) : Ajoute un élément au sommet
— depiler() : Retire et retourne l’élément du sommet
— sommet() : Retourne l’élément du sommet sans le retirer
— estv ide() : T estesilapileestvide
Invariant fondamental : Les éléments sont retirés dans l’ordre inverse de leur ajout.
6.2 Implémentation défensive
class Pile :
"""
Une pile ( stack ) avec programmation d f e n s i v e .
Invariants :
- self . elements est une liste
- Les lments sont s t o c k s en ordre LIFO
- La longueur ne d p a s s e pas une limite ( si f i x e )
"""
def __init__ ( self , capacite_max = None ) :
"""
Initialise une pile vide .
Pr conditions :
- capacite_max est None ou un entier positif
Postconditions :
- La pile est vide
- est_vide () retourne True
"""
assert capacite_max is None or \
( isinstance ( capacite_max , int ) and capacite_max > 0) , \
" capacite_max doit tre None ou un entier positif "
self . elements = []
self . capacite_max = capacite_max
def est_vide ( self ) :
"""
Teste si la pile est vide .
Postconditions :
- Retourne True si et seulement si aucun lment
"""
return len ( self . elements ) == 0
MP2I
Discipline de Programmation et Programmation Défensive 14
def empiler ( self , element ) :
"""
Empile un lment .
Pr conditions :
- element est compatible avec les lments existants
- Si capacite_max est f i x e , la pile n ’ est pas pleine
Postconditions :
- element est maintenant au sommet de la pile
- sommet () retournera element
- La taille augmente de 1
"""
# P r c o n d i t i o n : pas de d p a s s e m e n t de c a p a c i t
if self . capacite_max is not None :
assert len ( self . elements ) < self . capacite_max , \
f " Pile pleine ( c a p a c i t max : { self . capacite_max }) "
taille_avant = len ( self . elements )
self . elements . append ( element )
# Postconditions
assert len ( self . elements ) == taille_avant + 1 , \
" Postcondition : taille doit augmenter de 1 "
assert self . sommet () == element , \
" Postcondition : element doit tre au sommet "
def depiler ( self ) :
"""
Retire et retourne l ’ lment du sommet .
Pr conditions :
- La pile n ’ est pas vide
Postconditions :
- Retourne l ’ lment qui tait au sommet
- Cet lment est r e t i r de la pile
- La taille diminue de 1
"""
assert not self . est_vide () , \
" P r c o n d i t i o n : impossible de d p i l e r d ’ une pile vide "
taille_avant = len ( self . elements )
element = self . elements . pop ()
# Postconditions
assert len ( self . elements ) == taille_avant - 1 , \
" Postcondition : taille doit diminuer de 1 "
return element
def sommet ( self ) :
"""
MP2I
Discipline de Programmation et Programmation Défensive 15
Retourne l ’ lment du sommet sans le retirer .
Pr conditions :
- La pile n ’ est pas vide
Postconditions :
- Retourne l ’ lment du sommet
- La pile n ’ est pas m o d i f i e
"""
assert not self . est_vide () , \
" P r c o n d i t i o n : pile vide , pas d ’ lment au sommet "
return self . elements [ -1]
def taille ( self ) :
" " " Retourne le nombre d ’ lments dans la pile . " " "
return len ( self . elements )
# ============ Tests =============
def tester_pile () :
" " " Teste la pile avec assertions . " " "
# Test 1 : C r a t i o n et v r i f i c a t i o n d ’ une pile vide
pile = Pile ()
assert pile . est_vide () , " Une pile c r e doit tre vide "
assert pile . taille () == 0 , " Taille initiale doit tre 0 "
# Test 2 : Empiler et v r i f i e r
pile . empiler (10)
assert not pile . est_vide () , " A p r s empiler , pile n ’ est pas vide "
assert pile . sommet () == 10 , " Sommet doit tre 10 "
assert pile . taille () == 1 , " Taille doit tre 1 "
# Test 3 : Empiler plusieurs lments
pile . empiler (20)
pile . empiler (30)
assert pile . taille () == 3 , " Taille doit tre 3 "
assert pile . sommet () == 30 , " Sommet doit tre 30 ( LIFO ) "
# Test 4 : D p i l e r dans le bon ordre
assert pile . depiler () == 30 , " Premier d p i l a g e : 30 "
assert pile . depiler () == 20 , " D e u x i m e d p i l a g e : 20 "
assert pile . sommet () == 10 , " A p r s 2 d p i l a g e s , sommet : 10 "
# Test 5 : D p i l e r le dernier lment
assert pile . depiler () == 10 , " Dernier d p i l a g e : 10 "
assert pile . est_vide () , " A p r s d p i l a g e complet , pile vide "
# Test 6 : Erreur - d p i l e r d ’ une pile vide
try :
pile . depiler ()
MP2I
Discipline de Programmation et Programmation Défensive 16
assert False , " Devrait lever une AssertionError "
except AssertionError as e :
assert " vide " in str ( e ) . lower () , \
" Message d ’ erreur doit mentionner que la pile est vide "
# Test 7 : C a p a c i t limit e
pile_limitee = Pile ( capacite_max =2)
pile_limitee . empiler (1)
pile_limitee . empiler (2)
try :
pile_limitee . empiler (3)
assert False , " Devrait lever une AssertionError "
except AssertionError as e :
assert " pleine " in str ( e ) . lower () , \
" Message d ’ erreur doit mentionner que la pile est pleine "
print ( " Tous les tests sont p a s s s ! " )
# E x c u t e r les tests
if __name__ == " __main__ " :
tester_pile ()
MP2I
Discipline de Programmation et Programmation Défensive 17
7 Récapitulatif et Règles d’Or
7.1 Les 10 Règles d’Or de la Discipline de Programmation
1. Toujours spécifier : Écrivez une spécification claire avant d’implémenter
2. Expliciter les contrats : Documentez préconditions et postconditions
3. Vérifier les préconditions : Utilisez des assertions au début des fonctions
4. Vérifier les postconditions : Utilisez des assertions avant chaque return
5. Protéger les invariants : Maintenez les propriétés du système
6. Messages clairs : Les messages d’assertion doivent être informatifs
7. Fail fast : Échouez dès qu’une violation est détectée
8. Pas de coûts inutiles : Les assertions vérifient la logique, pas la performance
9. Séparer validation et assertions : Utilisez try/except pour les entrées, assertions
pour la logique
10. Tester régulièrement : Les assertions ne remplacent pas les tests unitaires
7.2 Résumé des concepts
Concept Qui vérifie ? Quand ?
Spécification Analyste Avant l’implémentation
Précondition Appelant Avant l’appel
Postcondition Fonction Après l’exécution
Assertion Programme À l’exécution
Invariant Classe/Système Toujours
7.3 Exemple final : Fonction de recherche dichotomique
def re che rc h e _ d i c h o t o m i q u e ( liste , cible ) :
"""
Recherche cible dans une liste t r i e par dichotomie .
Sp cification :
Retourne l ’ indice de cible ou -1 si absent .
Pr conditions :
- liste est une liste t r i e en ordre croissant
- liste est non vide
- cible est comparable aux lments de liste
Postconditions :
- Si retour >= 0 : liste [ retour ] == cible
- Si retour == -1 : cible n ’ est pas dans liste
- C o m p l e x i t : O ( log n )
"""
# V r i f i e r les p r c o n d i t i o n s
assert isinstance ( liste , list ) and len ( liste ) > 0 , \
" liste doit tre non vide "
# V r i f i e r que la liste est t r i e ( invariant )
MP2I
Discipline de Programmation et Programmation Défensive 18
for i in range ( len ( liste ) - 1) :
assert liste [ i ] <= liste [ i + 1] , \
f " P r c o n d i t i o n : liste doit tre tri e. " \
f " Violation l ’ index { i }: { liste [ i ]} > { liste [ i +1]} "
gauche , droite = 0 , len ( liste ) - 1
while gauche <= droite :
# Invariant de boucle :
# Si cible existe , elle est dans liste [ gauche : droite +1]
milieu = ( gauche + droite ) // 2
assert 0 <= milieu < len ( liste ) , \
f " Index de milieu invalide : { milieu } "
if liste [ milieu ] == cible :
# Postcondition
assert liste [ milieu ] == cible , \
" Postcondition v i o l e : encontre pas l ’ lment "
return milieu
elif liste [ milieu ] < cible :
gauche = milieu + 1
else :
droite = milieu - 1
# Postcondition : retourner -1 si pas t r o u v
assert cible not in liste , \
" Postcondition : si -1 est r e t o u r n , cible ne doit pas e x i s t
"
return -1
# Tests
liste_triee = [1 , 3 , 5 , 7 , 9 , 11 , 13 , 15]
assert r ec h e r c h e _ d i c h o t o m i q u e ( liste_triee , 7) == 3
assert r ec h e r c h e _ d i c h o t o m i q u e ( liste_triee , 1) == 0
assert r ec h e r c h e _ d i c h o t o m i q u e ( liste_triee , 15) == 7
assert r ec h e r c h e _ d i c h o t o m i q u e ( liste_triee , 6) == -1
print ( " Recherche dichotomique v a l i d e ! " )
MP2I
Discipline de Programmation et Programmation Défensive 19
8 Conclusion
La discipline de programmation n’est pas un simple ensemble de règles : c’est une
philosophie de la qualité du code. En établissant des contrats explicites entre les fonctions
et leurs utilisateurs, vous écrivez du code :
• Plus robuste : Les erreurs sont détectées au plus tôt
• Plus maintenable : Les intentions sont claires et documentées
• Plus debuggable : Les assertions pointent exactement où les choses vont mal
• Plus sûr : Les hypothèses sont vérifiées, pas devinées
La clé est de trouver l’équilibre : suffisamment d’assertions pour attraper les bugs, sans
surcharger le code de vérifications inutiles.
Mémorisez :
”Un bon programme n’est pas celui qui ne fait jamais d’erreurs,
c’est celui qui détecte et rapporte ses erreurs clairement.”
MP2I
Discipline de Programmation et Programmation Défensive 20
Exercices pratiques
Exercice 1 : Spécifier une fonction de maximum
Écrivez une spécification complète (description, préconditions, postconditions) pour une
fonction max(liste) qui retourne le plus grand élément.
Exercice 2 : Implémenter avec assertions
Implémentez une fonction echange(liste, i, j) qui échange deux éléments, avec :
— Assertions de précondition pour valider les indices
— Assertions de postcondition pour vérifier l’échange
— Assertions d’invariant pour vérifier que la somme reste constante
Exercice 3 : Déboguer avec assertions
Trouvez les bugs dans ce code et ajoutez les assertions appropriées :
def fusion_triees ( liste1 , liste2 ) :
" " " Fusionne deux listes t r i e s . " " "
resultat = []
i, j = 0, 0
while i < len ( liste1 ) and j < len ( liste2 ) :
if liste1 [ i ] <= liste2 [ j ]:
resultat . append ( liste1 [ i ])
i += 1
else :
resultat . append ( liste2 [ j ])
j += 1
# Ajouter les lments restants
resultat . extend ( liste1 [ i :])
resultat . extend ( liste2 [ j :])
return resultat
Exercice 4 : Conception avec contrats
Concevez une classe Queue (file d’attente FIFO) avec :
— Une spécification complète de chaque méthode
— Des assertions pour toutes les préconditions et postconditions
— Des invariants de classe documentés
MP2I