Modularité et mise au point
Utiliser des modules documentés, construire des tests et expliquer les causes d’erreurs dans un programme Python.
Environ 40 minutes
- Lire une API
- Documenter un module
- Choisir des tests et corriger une erreur
Comment changer un calcul sans casser les programmes qui l’utilisent ?
Un projet devient difficile à corriger lorsque calculs, affichages et lecture des données sont mélangés. La modularité attribue des responsabilités à des parties distinctes et fixe leurs interfaces. La mise au point s’appuie alors sur des spécifications, des tests choisis et une lecture méthodique des erreurs. Prérequis : fonctions, listes, conditions, exceptions simples et imports en Python. Nous travaillerons sur de petits programmes locaux, sans service externe ni donnée personnelle.
Séparer interface et réalisation
Un module regroupe des fonctions, classes ou constantes destinées à un même rôle. Dans notre exemple, mesures.py contient les calculs, programme.py organise leur utilisation, et tests.py vérifie les résultats. Le nom du fichier sans .py sert au moment de l’import. Une bibliothèque rassemble des éléments réutilisables ; certaines bibliothèques sont fournies avec Python, d’autres doivent être ajoutées dans un environnement adapté.
Une API, ou interface de programmation, décrit ce qu’un programme peut appeler : noms disponibles, paramètres, valeurs de retour, erreurs possibles et règles d’utilisation. Elle ne se limite pas à un service accessible sur Internet. Une fonction de calcul dans un module local possède déjà une interface. Le client doit connaître ce contrat, sans dépendre de chaque détail du calcul interne.
Pour moyenne(valeurs), spécifions une liste non vide de nombres finis, une valeur numérique renvoyée, et aucune modification de la liste reçue. Les nombres représentent des mesures dans une même unité. La moyenne est la somme divisée par le nombre d’éléments. Sans précondition ou gestion explicite du vide, une division par zéro rendrait l’interface ambiguë.
Dans programme.py, import mesures permet d’écrire mesures.moyenne([8, 12]). La forme from mesures import moyenne autorise ensuite moyenne([8, 12]), mais rend l’origine du nom moins visible. Évite import étoile, qui introduit de nombreux noms et peut masquer ceux déjà définis. Un fichier local nommé math.py peut aussi cacher le module standard math : le choix des noms compte.
Exemple résolu
Un module calcule une moyenne et affiche systématiquement le résultat. Un autre programme veut seulement récupérer le nombre. Que faut-il séparer ?
- Étape 1 : Le calcul doit renvoyer une valeur sans imposer sa présentation.
- Étape 2 : Le programme client choisit quand afficher et comment présenter l’unité.
- Étape 3 : Les tests vérifient directement la valeur renvoyée et l’absence de modification des entrées.
Résultat : On garde moyenne dans le module de calcul et on place print dans le programme qui utilise ce module.
Piège à éviter : Un affichage correct à l’écran ne prouve pas que la fonction renvoie le nombre attendu.
À toi d'essayer
Une fonction conserve son nom mais renvoie désormais une chaîne avec l’unité au lieu d’un nombre. Son interface est-elle conservée ?
Voir la réponseMasquer la réponse
Non. Le type de retour appartient au contrat ; un client qui additionne le résultat à un nombre peut désormais échouer. Il faut conserver le retour numérique ou annoncer et adapter explicitement le changement.
Lire une documentation et documenter
Avant d’utiliser une bibliothèque, cherche la signature et les hypothèses de la fonction. Vérifie le type attendu, l’unité, la convention de résultat, les exceptions et l’effet éventuel sur les entrées. Un exemple de documentation montre une utilisation, mais ne remplace pas les règles générales. Note aussi la version lorsque le comportement dépend de l’environnement.
La bibliothèque standard math fournit isclose pour comparer approximativement des nombres. Dans un petit contrôle numérique, math.isclose(obtenu, attendu, rel_tol=1e-9, abs_tol=1e-12) signifie que l’on accepte une différence suffisamment petite selon ces tolérances. Près de zéro, la tolérance absolue joue un rôle décisif. Les tolérances doivent correspondre à la précision attendue ; les agrandir jusqu’à obtenir un test vert viderait ce test de son sens.
Une docstring est une chaîne placée au début d’une fonction, d’une classe ou d’un module. Elle doit décrire le service rendu, les entrées admises, le résultat et les effets utiles à connaître. Un commentaire interne explique plutôt une décision d’implémentation. Répéter mot pour mot une instruction évidente n’aide pas à comprendre le contrat.
Voici une réalisation de moyenne. Le contrat exclut les listes vides et les valeurs non numériques ; ValueError rend néanmoins explicite le cas vide rencontré par erreur. La fonction lit la liste sans la trier ni la modifier. La somme en flottants peut subir des arrondis ; elle n’est pas présentée comme un calcul décimal exact.
Python
def moyenne(valeurs):
"""Moyenne de nombres finis, sans modifier la liste."""
if len(valeurs) == 0:
raise ValueError("liste vide")
return sum(valeurs) / len(valeurs)
Préparer un module utilisable
- Écris le contrat de chaque fonction publique avant de choisir les variables internes.
- Regroupe des responsabilités cohérentes ; garde les saisies et affichages hors des calculs réutilisables.
- Ajoute une documentation qui permet d’utiliser la fonction sans lire tout son corps.
- Vérifie que l’import ne lance pas une saisie ou une longue démonstration. Un programme principal peut être protégé par le test habituel sur __name__.
Choisir des tests qui contestent le contrat
Un test associe une entrée, un résultat attendu établi indépendamment et une comparaison avec le résultat observé. Un jeu de tests utile comprend des cas ordinaires, des limites et des cas qui distinguent plusieurs implémentations plausibles. Aucun ensemble fini de tests ne prouve à lui seul la correction pour toutes les entrées d’un domaine infini.
Pour moyenne, la liste [8, 12] doit donner 10, une liste singleton [-4] doit donner −4, et [0, 0] doit donner zéro. Le cas vide doit provoquer ValueError dans cette réalisation. Pour vérifier l’absence d’effet de bord, conserve une copie avant l’appel et compare ensuite la liste originale à cette copie. Un résultat numérique correct peut cacher une mutation indésirable.
Les assertions permettent un contrôle simple : assert moyenne([8, 12]) == 10 vérifie ici une égalité exacte avec des nombres faciles à représenter. Pour d’autres résultats flottants, utilise une comparaison approchée justifiée. Les assertions peuvent être désactivées dans certains modes d’exécution ; elles servent aux vérifications de développement et ne remplacent pas une validation nécessaire des données reçues.
Exemple résolu
Une alerte doit être vraie exactement pour 10 ≤ mesure < 20. Propose des tests qui vérifient les frontières.
- Étape 1 : Teste une valeur intérieure, comme 15, pour laquelle le résultat attendu est True.
- Étape 2 : Teste 10 puis 20 : le premier doit donner True, le second False.
- Étape 3 : Ajoute 9 et 21 pour vérifier les deux régions extérieures.
Résultat : Les attentes sont False, True, True, False, False pour les mesures 9, 10, 15, 20, 21.
Piège à éviter : Tester seulement 15 ne distingue pas les variantes incorrectes qui incluent ou excluent les mauvaises bornes.
À toi d'essayer
Un test appelle une fonction censée ne pas modifier une liste. Le résultat numérique est bon, mais la liste est désormais triée. Quel contrôle manque et quelle responsabilité est violée ?
Correction détaillée pas à pasDans ce chapitre : 2 corrections détaillées.
Passer d’un échec à sa cause
La mise au point commence par un cas reproductible aussi petit que possible. Distingue erreur de syntaxe, exception à l’exécution et résultat faux sans exception. Lis la trace de l’exception jusqu’à la ligne concernée ; elle indique où l’erreur devient visible, pas nécessairement où sa cause a été introduite. Une valeur incorrecte peut provenir d’un appel précédent.
Plusieurs familles d’erreurs reviennent souvent. Une chaîne utilisée comme nombre provoque une incompatibilité de types dans une addition. Une liste de longueur n possède les indices de zéro à n − 1 ; utiliser l’indice n déborde. Une condition qui oublie l’égalité à une borne laisse un cas mal classé. Deux variables de noms trop voisins favorisent une confusion. Une liste partagée entre deux noms peut être modifiée par un effet de bord.
Avec des flottants, 0.1 + 0.2 n’est pas exactement égal à 0.3 dans le calcul binaire usuel. Ce n’est pas une preuve que toutes les comparaisons numériques doivent être approximatives : les indices et les comptes entiers gardent des comparaisons exactes. Identifie la nature des données avant de choisir le contrôle.
Exemple résolu
Une fonction doit renvoyer le premier nombre strictement positif d’une liste, ou None s’il n’y en a pas. Elle renvoie None sur [0, −2, 5]. Une version fautive place return None à l’intérieur de la boucle. Explique et corrige.
- Étape 1 : Au premier élément, zéro n’est pas strictement positif ; le return None quitte pourtant immédiatement la fonction.
- Étape 2 : La recherche doit continuer sur les éléments suivants : −2, puis 5.
- Étape 3 : Place return None après la boucle, aligné avec for, pour conclure seulement après l’examen complet.
Résultat : La fonction corrigée renvoie 5 pour cette liste. Elle renvoie None pour la liste vide et pour une liste sans élément strictement positif.
Piège à éviter : Remplacer > par >= accepterait zéro et changerait la spécification au lieu de réparer le retour trop tôt.
Python
def premier_positif(valeurs):
for valeur in valeurs:
if valeur > 0:
return valeur
return None
À toi d'essayer
Deux versions de premier_positif donnent les mêmes résultats sur toutes les listes de longueur un. Construis une entrée qui distingue la version correcte de celle qui quitte la boucle après le premier échec.
Correction détaillée pas à pasDans ce chapitre : 2 corrections détaillées.
Au bac : expliquer une correction
- Rappelle ce que la fonction doit faire ; précise le cas qui contredit le résultat attendu.
- Déroule les instructions sur cette entrée et repère l’instant où le comportement diverge du contrat.
- Propose une modification minimale et explique sa portée, notamment l’emplacement et l’indentation des retours.
- Rejoue le cas fautif et des cas de frontière ; conserve ce test pour détecter le retour du même défaut.
Organiser les changements et retenir
Une fois la correction obtenue, rejoue aussi les tests des fonctions qui utilisent le module. Un test unitaire isole un calcul ; un test d’intégration vérifie que plusieurs parties coopèrent. Un import réussi ne prouve donc pas le fonctionnement du programme complet. La compatibilité se juge sur le contrat réellement consommé.
La gestion de versions aide à expliquer et annuler un changement. Enregistre une modification cohérente avec un message clair et la raison du correctif. Elle ne remplace ni le choix des tests ni la documentation. Les dépendances circulaires compliquent les imports et peuvent provoquer des erreurs d’initialisation ; elles ne provoquent pas systématiquement une erreur dans toute situation Python. Une structure simple des dépendances reste plus facile à raisonner.
Vocabulaire
- Interface :
- Contrat de services proposés aux programmes clients.
- Effet de bord :
- Modification observable en plus de la valeur renvoyée, par exemple une mutation de liste.
- Régression :
- Réapparition d’un défaut ou perte d’un comportement auparavant correct après une modification.
- Cas limite :
- Entrée située à une frontière du domaine ou d’une condition.
Relier les besoins aux mécanismes
| Besoin | Réponse |
|---|---|
| Utiliser | Lire la signature et les règles avant l’appel. |
| Réutiliser | Isoler les responsabilités et documenter le contrat. |
| Corriger | Reproduire, comprendre, modifier et rejouer les tests. |
À retenir
- L’interface inclut entrées, sorties, erreurs et effets de bord.
- Un test vérifie un cas ; il ne garantit pas tous les cas possibles.
- Une frontière incluse et une frontière exclue demandent des attentes distinctes.
Fiche de révision
Avant l’import, identifie les noms exposés et leurs contrats. Après le changement, vérifie les programmes clients.
Pour une recherche, place le constat d’absence après l’examen complet ; une sortie anticipée ne convient que lorsqu’un résultat est trouvé.
Une correction convaincante relie un échec reproductible, sa cause et des vérifications ciblées.
Questions fréquentes
Une API est-elle toujours un service web ?
Non. Une interface locale de bibliothèque propose elle aussi des fonctions et des règles d’appel.
Faut-il tester seulement les cas qui fonctionnent déjà ?
Non. Cherche aussi les bornes, les entrées qui déclenchent l’erreur et les effets de bord que le contrat interdit.
Et maintenant ?
Tu as lu le cours. L’étape suivante tient en une action.
Relis le coursReprends l’essentiel avant de passer à la suite.Prolonge l’expérience !
Continue à explorer ce chapitre à travers de nouvelles activités, des défis et des expériences interactives. Avec Premium, l’apprentissage continue !
Ici, tu débloques aussi 2 corrections détaillées.
Parent ? Suivez les progrès de votre enfant.
