Aller au contenu

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

★☆☆Environ 5 min

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)
  1. Pourquoi faut-il écrire geometrie. devant le nom de la fonction ?
  2. 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
  1. Le programme principal ne contient pas la définition de aire_rectangle : elle vit dans le module. Le préfixe geometrie. indique où aller chercher la fonction, et évite aussi les confusions si un autre module possède une fonction du même nom.

  2. La fonction renvoie largeur * longueur, donc ici 4×7=284 \times 7 = 28.

  3. print affiche cette valeur renvoyée : le programme affiche donc 28.

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

★☆☆Environ 8 min

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()
  1. Quelles fonctions forment l'interface que le module propose aux autres programmes ?
  2. Que représentent _contacts et _normaliser ?
  3. 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
  1. L'interface se compose de ajouter et de chercher : ce sont les fonctions documentées, destinées aux utilisateurs.

  2. _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 ».

  3. Écrire directement dans _contacts contourne 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 à ajouter continueraient de marcher.

  4. 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

★★☆Environ 10 min

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).

  1. Quelle est la valeur renvoyée, en euros ?
  2. La fonction total_ttc utilise prix_ttc : pourquoi est-ce un avantage de ne pas avoir recopié la formule ?
Indice 1

Calcule d'abord chaque prix TTC avec le coefficient 1+20/1001 + 20/100.

Indice 2

Imagine que la formule de calcul doive changer.

Voir la correction pas à pas
  1. Le coefficient vaut 1+20100=1,21 + \frac{20}{100} = 1{,}2.

  2. Les trois prix TTC sont : 50×1,2=6050 \times 1{,}2 = 60 ; 20×1,2=2420 \times 1{,}2 = 24 ; 10×1,2=1210 \times 1{,}2 = 12.

  3. Leur somme est 60+24+12=9660 + 24 + 12 = 96 euros.

  4. 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

★★☆Environ 10 min

Un module pile.py définit une classe Pile dont l'interface est :

  • Pile() : crée une pile vide ;
  • empiler(x) : ajoute x au sommet ;
  • depiler() : retire et renvoie l'élément du sommet ;
  • est_vide() : renvoie True si 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()
  1. Quelle est la valeur de c à la fin ?
  2. 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
  1. Après les trois empiler, la pile contient 5, 8, 3 (le sommet est à droite).

  2. depiler() renvoie 3 : a vaut 3 et la pile contient 5, 8.

  3. empiler(4) donne 5, 8, 4.

  4. Le deuxième depiler() renvoie 4 : b vaut 4 et la pile contient 5, 8.

  5. Le troisième depiler() renvoie 8 : c vaut 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

★★☆Environ 12 min

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().

  1. Que s'affiche-t-il avec la version A ? Avec la version B ?
  2. Le programme doit-il être modifié quand on passe de A à B ?
  3. 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
  1. Version A : _n passe de 0 à 3, donc valeur() renvoie 3.

  2. Version B : la liste reçoit trois éléments, donc len(_traces) vaut 3 : même résultat.

  3. Les deux versions offrent les mêmes fonctions incrementer et valeur, avec le même comportement visible. Le programme appelant n'a donc aucune modification à subir.

  4. 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

★★★Environ 15 min

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 de chaine ;
  • inserer(t, i, c) : insère le caractère c à la position i ;
  • supprimer(t, i) : retire le caractère situé à la position i ;
  • remplacer(t, i, c) : remplace le caractère situé à la position i par c ;
  • 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))
  1. Quelle est la longueur affichée ?
  2. 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
  1. creer("banane") donne ['b','a','n','a','n','e'], de longueur 6.

  2. inserer(t, 0, "r") : ['r','b','a','n','a','n','e'], longueur 7.

  3. remplacer(t, 1, "c") : le caractère d'indice 1 (le 'b') devient 'c' : ['r','c','a','n','a','n','e'].

  4. supprimer(t, 6) : on retire l'indice 6, soit le 'e' final : ['r','c','a','n','a','n'], longueur 6.

  5. inserer(t, 3, "o") : ['r','c','a','o','n','a','n'], longueur 7.

  6. La longueur affichée est donc 7, et vers_chaine renvoie "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 6+1−1+1=76 + 1 - 1 + 1 = 7.

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

  1. Modularité et API — Spécialité NSI Terminale 2024-2025, Lycée La Martinière Diderot (ac-lyon.fr). Consulté le 7 novembre 2026.
  2. Introduction à la Modularité et API — NSI Terminale 2024-2025, Lycée La Martinière Diderot (ac-lyon.fr). Consulté le 7 novembre 2026.
  3. 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.

Signaler une erreur sur cette page

Je suis en…

Primaire

  • CP Bientôt
  • CE1 Bientôt
  • CE2 Bientôt
  • CM1 Bientôt
  • CM2 Bientôt

Collège

Lycée

Supérieur

  • BTS Bientôt
  • BUT Bientôt
  • Licence Bientôt
  • Prépas Bientôt

Sur cette page