Numérique et sciences informatiques (spécialité) · TerminaleBac
Modularité et API : exercices corrigés de Terminale NSI
Publié le 7 novembre 2026 · Mis à jour le 7 novembre 2026 · Signaler une erreur
L'essentiel
- Un gros projet se découpe proprement en parties : c'est le principe de la modularité 3.
- Chaque module montre une interface (les fonctions à appeler) et cache son fonctionnement interne.
- Pour utiliser un module, tu lis ses signatures et ses spécifications, pas son code.
- Tant que l'interface ne change pas, tu peux réécrire l'intérieur d'un module sans toucher aux programmes qui l'utilisent.
- Dans une trace d'exécution, suis l'état du module appel après appel.
Comment utiliser ces exercices
Cette page te fait pratiquer la modularité : séparer un programme en parties distinctes et bien rangées. Ce principe n'est pas une nouveauté de la Terminale, tu l'as déjà rencontré en Première ; il s'agit maintenant de l'expliciter davantage, car c'est une méthode essentielle de programmation 2. Elle devient indispensable dès que le projet est « gros » : on sépare alors proprement ses différentes parties 3.
Pour les exercices, nous adoptons le vocabulaire de travail suivant. Un module est un fichier Python qui regroupe des fonctions (et parfois des classes) sur un même thème. Son interface, souvent appelée API, est la liste de ce qu'un autre programme a le droit d'appeler : noms, paramètres, valeur renvoyée, comportement attendu. Le reste (variables internes, fonctions d'aide) est l'implémentation.
Rappel : lire une interface
Les exercices
Exercice 1 Appeler une fonction d'un module
On dispose du module geometrie.py donné dans le rappel, avec la fonction aire_rectangle(largeur, longueur) qui renvoie le produit des deux paramètres.
Un programme principal contient :
import geometrie
resultat = geometrie.aire_rectangle(4, 7)
print(resultat)
- Pourquoi faut-il écrire
geometrie.devant le nom de la fonction ? - Quelle valeur est affichée ?
Indice 1
Le préfixe indique dans quel espace de noms se trouve la fonction.
Indice 2
La fonction multiplie ses deux paramètres.
Voir la correction pas à pas
Le programme principal ne contient pas la définition de
aire_rectangle: elle vit dans le module. Le préfixegeometrie.indique où aller chercher la fonction, et évite aussi les confusions si un autre module possède une fonction du même nom.La fonction renvoie
largeur * longueur, donc ici .printaffiche cette valeur renvoyée : le programme affiche donc28.
Le programme principal n'a utilisé que le nom et les paramètres de la fonction. Il ignore comment elle calcule, et n'en a pas besoin : l'aire vaut 28.
Exercice 2 Distinguer interface et implémentation
Un module annuaire.py contient le code suivant :
_contacts = {}
def ajouter(nom, numero):
"""Enregistre le numéro associé au nom."""
_contacts[nom] = numero
def chercher(nom):
"""Renvoie le numéro associé au nom, ou None s'il est inconnu."""
return _contacts.get(nom)
def _normaliser(texte):
return texte.strip().lower()
- Quelles fonctions forment l'interface que le module propose aux autres programmes ?
- Que représentent
_contactset_normaliser? - Un programmeur écrit
annuaire._contacts["Léa"] = "0102030405"depuis son programme principal. Pourquoi est-ce une mauvaise idée ?
Indice 1
Par convention, un nom qui commence par un tiret bas signale un élément interne.
Indice 2
Que se passerait-il si l'auteur du module remplaçait le dictionnaire par une autre structure ?
Voir la correction pas à pas
L'interface se compose de
ajouteret dechercher: ce sont les fonctions documentées, destinées aux utilisateurs._contacts(la donnée) et_normaliser(la fonction d'aide) font partie de l'implémentation. Le tiret bas initial est une convention qui prévient : « usage interne ».Écrire directement dans
_contactscontourne l'interface. Si l'auteur du module remplace un jour le dictionnaire par une liste, une base de données ou autre chose, ce code extérieur cesse de fonctionner, alors que les appels àajoutercontinueraient de marcher.La bonne écriture est
annuaire.ajouter("Léa", "0102030405").
Passer par l'interface protège le programme utilisateur des modifications internes. C'est tout l'intérêt de séparer ce qui est promis de ce qui est caché.
Exercice 3 Un module de calcul de prix
Le module boutique.py propose :
def prix_ttc(prix_ht, taux):
"""Prix toutes taxes comprises, taux en pourcentage."""
return prix_ht * (1 + taux / 100)
def total_ttc(liste_prix_ht, taux):
"""Somme des prix TTC d'une liste de prix hors taxes."""
total = 0
for p in liste_prix_ht:
total = total + prix_ttc(p, taux)
return total
Un programme appelle boutique.total_ttc([50, 20, 10], 20).
- Quelle est la valeur renvoyée, en euros ?
- La fonction
total_ttcutiliseprix_ttc: pourquoi est-ce un avantage de ne pas avoir recopié la formule ?
Indice 1
Calcule d'abord chaque prix TTC avec le coefficient .
Indice 2
Imagine que la formule de calcul doive changer.
Voir la correction pas à pas
Le coefficient vaut .
Les trois prix TTC sont : ; ; .
Leur somme est euros.
Réutiliser
prix_ttcévite de dupliquer la formule : si elle devait être corrigée, une seule modification suffirait et les deux fonctions resteraient cohérentes.
La valeur renvoyée est 96 euros. Découper en petites fonctions qui s'appuient les unes sur les autres rend un module plus facile à corriger et à tester.
Exercice 4 Suivre l'état d'une pile
Un module pile.py définit une classe Pile dont l'interface est :
Pile(): crée une pile vide ;empiler(x): ajoutexau sommet ;depiler(): retire et renvoie l'élément du sommet ;est_vide(): renvoieTruesi la pile est vide.
Un programme exécute :
from pile import Pile
p = Pile()
p.empiler(5)
p.empiler(8)
p.empiler(3)
a = p.depiler()
p.empiler(4)
b = p.depiler()
c = p.depiler()
- Quelle est la valeur de
cà la fin ? - Que contient la pile à la fin ?
Indice 1
Une pile rend toujours en premier le dernier élément ajouté.
Indice 2
Note le contenu de la pile, du fond vers le sommet, après chaque instruction.
Voir la correction pas à pas
Après les trois
empiler, la pile contient 5, 8, 3 (le sommet est à droite).depiler()renvoie 3 :avaut 3 et la pile contient 5, 8.empiler(4)donne 5, 8, 4.Le deuxième
depiler()renvoie 4 :bvaut 4 et la pile contient 5, 8.Le troisième
depiler()renvoie 8 :cvaut 8, et il reste uniquement 5 dans la pile.
La valeur de c est 8 et la pile contient seulement 5. Tu as raisonné sans connaître la structure interne (liste, chaîne de cellules...) : l'interface suffit.
Exercice 5 Changer d'implémentation sans casser le reste
Voici deux versions d'un module compteur.py.
Version A
_n = 0
def incrementer():
global _n
_n = _n + 1
def valeur():
return _n
Version B
_traces = []
def incrementer():
_traces.append(1)
def valeur():
return len(_traces)
Un programme appelle trois fois incrementer() puis affiche valeur().
- Que s'affiche-t-il avec la version A ? Avec la version B ?
- Le programme doit-il être modifié quand on passe de A à B ?
- Donne un cas où la version A serait préférable.
Indice 1
Compare ce que voit le programme appelant, pas ce qui se passe à l'intérieur.
Indice 2
Pense à la place occupée en mémoire après un très grand nombre d'appels.
Voir la correction pas à pas
Version A :
_npasse de 0 à 3, doncvaleur()renvoie 3.Version B : la liste reçoit trois éléments, donc
len(_traces)vaut 3 : même résultat.Les deux versions offrent les mêmes fonctions
incrementeretvaleur, avec le même comportement visible. Le programme appelant n'a donc aucune modification à subir.La version A occupe une place constante, alors que la liste de la version B grandit à chaque appel. Pour des millions d'appels, A est préférable ; B pourrait servir si l'on voulait conserver un historique.
Les deux versions affichent 3. Une même interface peut être réalisée de plusieurs façons : c'est ce qui permet d'améliorer un module sans toucher aux programmes qui s'en servent.
Exercice 6 Un module de texte modifiable
On veut manipuler un texte que l'on peut modifier caractère par caractère. Un module texte_modifiable.py conserve le texte sous forme de liste de caractères, et propose :
creer(chaine): renvoie la liste des caractères dechaine;inserer(t, i, c): insère le caractèrecà la positioni;supprimer(t, i): retire le caractère situé à la positioni;remplacer(t, i, c): remplace le caractère situé à la positioniparc;vers_chaine(t): renvoie le texte sous forme de chaîne.
On exécute :
import texte_modifiable as tm
t = tm.creer("banane")
tm.inserer(t, 0, "r")
tm.remplacer(t, 1, "c")
tm.supprimer(t, 6)
tm.inserer(t, 3, "o")
print(len(t))
print(tm.vers_chaine(t))
- Quelle est la longueur affichée ?
- Quel texte est affiché ?
Indice 1
Les positions commencent à 0.
Indice 2
Réécris la liste complète après chaque instruction.
Voir la correction pas à pas
creer("banane")donne ['b','a','n','a','n','e'], de longueur 6.inserer(t, 0, "r"): ['r','b','a','n','a','n','e'], longueur 7.remplacer(t, 1, "c"): le caractère d'indice 1 (le 'b') devient 'c' : ['r','c','a','n','a','n','e'].supprimer(t, 6): on retire l'indice 6, soit le 'e' final : ['r','c','a','n','a','n'], longueur 6.inserer(t, 3, "o"): ['r','c','a','o','n','a','n'], longueur 7.La longueur affichée est donc 7, et
vers_chainerenvoie "rcaonan".
La longueur affichée est 7 et le texte est rcaonan. Le résultat a été obtenu en suivant l'état de la liste instruction par instruction, sans rien savoir d'autre que l'interface du module. Contrôle la longueur par un bilan : deux insertions, une suppression et un remplacement sans effet sur la taille donnent .
Les questions qu'on se pose
À quoi sert vraiment la modularité ?
Elle permet de séparer proprement les différentes parties d'un gros projet 3. Chaque partie peut alors être écrite, testée et corrigée séparément, et plusieurs personnes peuvent travailler en même temps sans se gêner.
Comment savoir si je dois créer un nouveau module ?
Pose-toi trois questions : ce groupe de fonctions traite-t-il d'un même thème ? Pourrait-on le réutiliser dans un autre programme ? Peut-on décrire en quelques lignes ce qu'il promet à ses utilisateurs ? Trois « oui » sont un bon signe qu'un module distinct est justifié.
Pourquoi ne pas simplement tout écrire dans un seul fichier ?
Cela peut suffire pour un petit script. Mais quand le programme grossit, un seul fichier devient difficile à relire, à tester et à partager. Découper en modules à interface claire évite d'avoir à tout comprendre pour modifier une seule partie.
Sources
- Modularité et API — Spécialité NSI Terminale 2024-2025, Lycée La Martinière Diderot (ac-lyon.fr). Consulté le 7 novembre 2026.
- Introduction à la Modularité et API — NSI Terminale 2024-2025, Lycée La Martinière Diderot (ac-lyon.fr). Consulté le 7 novembre 2026.
- Modularité en programmation — NSI Terminale 2026-2027, Lycée La Martinière Diderot (ac-lyon.fr). Consulté le 7 novembre 2026.
Les numéros dans le texte renvoient à ces sources. Une source qui ne dit pas ce qu'on lui fait dire ? Signale-le.