EuraStudy
Fiches/NSI — Numérique et sciences informatiques/Langages et paradigmes de programmation
Fiches · NSI — Numérique et sciences informatiquesFR · Bac

Langages et paradigmes de programmation

Ce thème de terminale aborde les grands styles de programmation — impératif, fonctionnel et objet — et met l'accent sur la PROGRAMMATION ORIENTÉE OBJET (classes, attributs, méthodes, instances, constructeur \_\_init\_\_ et encapsulation) ainsi que sur la MISE AU POINT des programmes : spécifier une fonction (préconditions, postconditions, documentation), écrire des jeux de tests, utiliser des assertions et gérer des exceptions. L'idée directrice : tous les langages usuels étant Turing-complets, choisir un paradigme relève du style et de la structuration, et un programme se conçoit avec son contrat puis s'éprouve méthodiquement par les tests.

5 sections·~27 min de lecture·4 compétences·Niveau Base 1 · Standard 3 · Approfondissement 1·Vérifié · 06/2026

T·0555 / 10
Profil d’examen
Distinguer, sur des exemples de code, les paradigmes impératif, fonctionnel et objet ; choisir un paradigme selon le champ d'application d'un programme, en expliquant que les langages usuels, tous Turing-complets, sont équivalents en pouvoir d'expression.Écrire une classe : définir des attributs et des méthodes, écrire le constructeur \_\_init\_\_, et instancier des objets ; utiliser un objet en accédant à ses attributs et en appelant ses méthodes.Spécifier une fonction par ses préconditions et postconditions et la documenter (docstring) ; distinguer la spécification (le quoi) de l'implémentation (le comment).Mettre au point un programme : prototyper une fonction, l'éprouver sur des jeux de tests pertinents (nominal, limite, erreur), utiliser assertions et exceptions, repérer et corriger les erreurs.
Opérateurs :distingueridentifierécrireinstancierutiliserspécifierdocumentertestercorrigerjustifier
Profondeur

Profondeur de lecture : Approfondi

Texte

Taille du texte : Standard

Sommaire · 5 sections▾
  1. Langages et paradigmes de programmation
    • 01Paradigmes de programmation : impératif, fonctionnel, objet○
    • 02Programmation orientée objet : classes, attributs, méthodes◐
    • 03Instancier et utiliser des objets en Python◐
    • 04Spécifier une fonction : préconditions, postconditions, documentation◐
    • 05Mettre au point : jeux de tests, assertions, exceptions●
§ 01

Paradigmes de programmation : impératif, fonctionnel, objet#

●○○BaseLPeduscol-programme-nsi-terminale

Trois paradigmes pour un même problème : sommer une liste

Trois paradigmes, un même problèmeTableau de 3 colonnes et 3 lignes, Données: Paradigme · Exemple : sommer L · Idée maîtresse; impératif · s=0 ; for x in L: s+=x · décrire les étapes et l'état (s change); fonctionnel · somme(L) = tête + somme(reste) · composer des fonctions, sans état mutable; objet · p = Pile(L) ; p.total() · structurer : données + méthodes dans un objet, cellule mise en évidence : structurer : données + méthodes dans un objetPARADIGMEEXEMPLE : SOMMER LIDÉE MAÎTRESSEimpératifs=0 ; for x in L: s+=xdécrire les étapes et l'état(s change)fonctionnelsomme(L) = tête +somme(reste)composer des fonctions, sansétat mutableobjetp = Pile(L) ; p.total()structurer : données +méthodes dans un objet
Fig. 1Le même problème, trois styles d'organisation. Tous Turing-complets : même pouvoir d'expression, manières de penser différentes. Le style objet est mis en évidence.

Points clés

Un PARADIGME de programmation est une manière de penser et d'organiser un programme — un « style » qui dicte comment on exprime les calculs et comment on structure le code. Le programme de terminale en distingue trois sur des exemples : impératif, fonctionnel et objet. Ce ne sont pas des langages mais des façons d'utiliser un langage.
Le paradigme IMPÉRATIF décrit le calcul comme une SUITE D'INSTRUCTIONS qui modifient un ÉTAT (le contenu des variables) : affectations, séquences, conditions, boucles. On dit « comment » faire, pas à pas. C'est le style enseigné en première (variables, for, while). Exemple : sommer une liste avec un accumulateur "s" que l'on met à jour dans une boucle.
Le paradigme FONCTIONNEL décrit le calcul comme la COMPOSITION et l'APPLICATION de fonctions, idéalement sans état mutable : on privilégie des fonctions « pures » (mêmes arguments → même résultat, sans effet de bord) et la récursivité plutôt que les boucles. On dit « quoi » calculer. Exemple : sommer une liste comme « tête + somme(reste) », l'élément neutre étant 0 sur la liste vide.
Le paradigme OBJET (programmation orientée objet, POO) STRUCTURE le programme autour d'OBJETS qui regroupent des données (attributs) et les traitements qui agissent sur elles (méthodes). On organise le code autour des « choses » du problème (un compte bancaire, un point, une pile) plutôt qu'autour des actions. C'est le cœur nouveau de la terminale.
Repère essentiel à connaître : tous les langages généralistes usuels (Python, Java, C, OCaml…) sont TURING-COMPLETS, donc ÉQUIVALENTS en pouvoir d'expression — tout ce qui est calculable dans l'un l'est dans les autres. Choisir un paradigme n'augmente donc pas ce que l'on peut calculer : c'est un choix de LISIBILITÉ, de structuration et de maintenance, adapté au problème.
Un même langage (Python notamment) est MULTI-PARADIGME : il permet d'écrire le même programme dans les trois styles, et on les combine couramment dans un projet réel. Le travail attendu n'est pas de classer un langage mais de RECONNAÎTRE, sur un extrait de code, le ou les paradigmes mis en œuvre.
somme(L)={0si L est videteˆte(L)+somme(reste(L))sinon\text{somme}(L) = \begin{cases} 0 & \text{si } L \text{ est vide} \\ \text{tête}(L) + \text{somme}(\text{reste}(L)) & \text{sinon} \end{cases}somme(L)={0teˆte(L)+somme(reste(L))​si L est videsinon​

Le style FONCTIONNEL : une définition récursive, sans variable mutable

La même tâche (sommer une liste) s'exprime sans boucle ni accumulateur : élément neutre 0 sur la liste vide, puis composition « tête + somme du reste ». À comparer avec la version impérative à accumulateur.

Choisir un paradigme, c'est choisir un style d'organisation

Choisir un paradigme = choisir un styleGraphe, Un problème → impératif (séquence, boucles, état), Un problème → fonctionnel (fonctions pures), Un problème → objet (classes, encapsulation)Un problèmeimpératif(séquence,boucles, état)fonctionnel(fonctionspures)objet (classes,encapsulation)
Fig. 2Un même problème peut s'aborder dans chacun des trois paradigmes ; un langage comme Python permet les trois et on les combine souvent. Le paradigme objet est mis en évidence.
Exemple corrigé

Reconnaître le paradigme d'un extrait de code

Pour chacun des trois extraits suivants, donner le paradigme dominant et justifier en citant l'indice décisif. (A) « s = 0 ; for x in L: s = s + x ; return s ». (B) « def somme(L): return 0 if L == [] else L[0] + somme(L[1:]) ». (C) « p = Pile() ; p.empiler(3) ; p.empiler(7) ; print(p.total()) ».

  1. 01Extrait (A)

    Une variable d'état "s" est initialisée puis modifiée à chaque tour d'une boucle for : on décrit pas à pas COMMENT obtenir le résultat en faisant évoluer l'état. Indice décisif : la boucle qui met à jour une variable.

  2. 02Extrait (B)

    La fonction somme s'appelle elle-même, sans aucune variable mutable ni effet de bord ; le résultat est une composition « L[0] + somme(L[1:]) » avec le cas de base 0 sur la liste vide. Indice décisif : récursivité d'une fonction pure.

  3. 03Extrait (C)

    On crée une instance p d'une classe Pile, puis on appelle des MÉTHODES (empiler, total) sur cet objet, qui encapsule ses données. Indice décisif : un objet sur lequel on appelle des méthodes avec la notation p.methode().

  4. 04Conclusion transversale

    Les trois extraits calculent la même somme : ils sont équivalents en pouvoir d'expression (tous les langages usuels sont Turing-complets). Le choix entre eux est un choix de style et de structuration.

Résultat : (A) impératif (boucle qui met à jour l'état s) ; (B) fonctionnel (fonction récursive pure, sans effet de bord) ; (C) objet (méthodes appelées sur une instance). Les trois résolvent le même problème : la différence est de style, pas de puissance de calcul.

Objectif Bac

  • Objectif Bac : sur un (ou deux) extraits de code donnés, IDENTIFIER et JUSTIFIER le paradigme employé (« impératif car il y a une boucle qui met à jour une variable d'état », « fonctionnel car la fonction est récursive et sans effet de bord », « objet car on définit une classe et on appelle des méthodes sur des instances »).
  • Objectif Bac : expliquer en une ou deux phrases ce que signifie « les langages usuels sont Turing-complets / équivalents », et en déduire que le choix d'un paradigme relève du style et de la structuration, non de la puissance de calcul.
  • Objectif Bac : CHOISIR un paradigme selon le champ d'application — justifier qu'un problème centré sur des « objets » du domaine (un compte, un point, une pile) appelle l'objet, qu'un traitement par transformations successives sans état se prête au fonctionnel, et qu'un calcul pas à pas pilotant un état relève de l'impératif.

Erreurs fréquentes

  • Croire qu'un langage « EST » un paradigme (« Python est impératif », « Java est objet »). La plupart des langages généralistes sont multi-paradigmes ; c'est le CODE écrit, pas le langage, qui relève d'un style donné.
  • Confondre « paradigme fonctionnel » et « écrire des fonctions ». Découper son programme en fonctions (def) est une bonne pratique dans TOUS les paradigmes ; le style fonctionnel se caractérise plutôt par l'absence d'effets de bord et l'usage de fonctions pures et de la récursivité.
  • Penser que l'objet est « plus puissant » que l'impératif. Tous ces styles ont le même pouvoir d'expression (Turing-complétude) ; l'objet apporte une meilleure ORGANISATION pour les gros programmes, pas de nouvelles capacités de calcul.

Révision active

On donne trois fonctions Python qui calculent toutes la factorielle de n : (1) une version avec une boucle for et une variable f mise à jour ; (2) une version récursive « fact(n) = 1 si n=0, sinon n*fact(n-1) » sans variable mutable ; (3) une classe Compteur munie d'une méthode factorielle() appelée sur une instance. Pour chacune, nommer le paradigme dominant et justifier en une phrase en citant l'indice décisif dans le code.

Rappel actif

Rappelle-toi les points clés — puis révèle.

Sources : Programme de spécialité NSI — classe terminale (voie générale), BO spécial n° 8 du 25 juillet 2019 (Ministère de l'Éducation nationale) · Numérique et sciences informatiques — programmes et ressources, voie générale (Éduscol) (Éduscol — Ministère de l'Éducation nationale)

§ 02

Programmation orientée objet : classes, attributs, méthodes#

●●○StandardLPeduscol-programme-nsi-terminale

La « carte d'identité » d'une classe : attributs et méthodes

Carte d'identité d'une classe : attributs + méthodesTableau de 3 colonnes et 6 lignes, Données: Classe « CompteBancaire » · Catégorie · Rôle; titulaire · attribut · l'état : le nom du titulaire; solde · attribut · l'état : le montant disponible; __init__ · méthode · construit l'objet et initialise l'état; deposer(m) · méthode · augmente le solde de m; retirer(m) · méthode · diminue le solde si possible; afficher() · méthode · le comportement : affiche l'état, cellule mise en évidence : __init__CLASSE «COMPTEBANCAIRE »CATÉGORIERÔLETITULAIREattributl'état : le nom du titulaireSOLDEattributl'état : le montantdisponible__INIT__méthodeconstruit l'objet etinitialise l'étatDEPOSER(M)méthodeaugmente le solde de mRETIRER(M)méthodediminue le solde si possibleAFFICHER()méthodele comportement : affichel'état
Fig. 3Une classe réunit un ÉTAT (les attributs : titulaire, solde) et un COMPORTEMENT (les méthodes). C'est la carte d'identité du type d'objet ; le constructeur __init__ est mis en évidence.

Points clés

Une CLASSE est un MODÈLE (un « moule ») qui décrit une catégorie d'objets : elle définit les données que chaque objet portera (les ATTRIBUTS) et les opérations qu'on pourra lui appliquer (les MÉTHODES). Un OBJET, ou INSTANCE, est un exemplaire concret produit à partir de ce moule. Vocabulaire clé : la classe est le moule, l'objet est l'exemplaire.
Un ATTRIBUT est une variable attachée à un objet : il représente une partie de son ÉTAT (ex. le solde d'un compte, les coordonnées d'un point). Une MÉTHODE est une fonction définie dans la classe et liée à l'objet : elle agit sur les attributs de cet objet (ex. deposer(montant) modifie le solde). Données et traitements sont ainsi réunis au même endroit.
Le CONSTRUCTEUR est la méthode spéciale qui INITIALISE un objet à sa création. En Python il s'écrit \_\_init\_\_(self, …) : il reçoit en premier paramètre self, qui désigne l'INSTANCE en cours de construction, et il crée les attributs en écrivant self.nom = valeur. self n'est pas un mot-clé magique : c'est simplement le premier paramètre, qui reçoit automatiquement l'objet courant à chaque appel de méthode.
L'ENCAPSULATION consiste à regrouper dans l'objet ses données ET les méthodes qui les manipulent, et à n'exposer qu'une INTERFACE (les méthodes publiques) en cachant les détails internes. On manipule alors l'objet par ses méthodes plutôt qu'en touchant directement ses attributs : le code utilisateur ne dépend pas de la représentation interne, ce qui rend le programme plus robuste et plus facile à faire évoluer.
En Python, on définit une classe avec « class NomClasse: » (par convention en CamelCase), puis ses méthodes en blocs def indentés ; chaque méthode reçoit self en premier paramètre. Les attributs ne sont pas déclarés à l'avance : ils sont créés dynamiquement, le plus souvent dans \_\_init\_\_, par des affectations self.attribut = ….
c = CompteBancaire("Ada", 100)  ⟹  c.solde=100\texttt{c = CompteBancaire("Ada", 100)} \;\Longrightarrow\; \texttt{c.solde} = 100c = CompteBancaire("Ada", 100)⟹c.solde=100

Instancier : du moule (classe) à l'exemplaire (objet)

L'appel CompteBancaire("Ada", 100) déclenche __init__ avec self = le nouvel objet ; les affectations self.titulaire = … et self.solde = … fixent l'état initial de l'instance c.

Le constructeur __init__ et le rôle de self

Le constructeur __init__ et le rôle de selfGraphe, c = CompteBancaire('Ada', 100) → __init__(self, titulaire, solde), __init__(self, titulaire, solde) → self = le nouvel objet, self = le nouvel objet → self.titulaire, self.solde initialisésc = CompteBancaire('Ada',100)init(self,)titulaire,solde)self = le nouvelobjetself.titulaire,self.soldeinitialisésappelle
Fig. 4Instancier appelle automatiquement __init__ ; self y désigne le nouvel objet en cours de construction, sur lequel on crée les attributs (self.titulaire, self.solde). Le rôle de self est mis en évidence.
Exemple corrigé

Écrire et instancier une classe CompteBancaire

Écrire une classe CompteBancaire avec : les attributs titulaire et solde ; un constructeur \_\_init\_\_(self, titulaire, solde_initial) ; une méthode deposer(self, montant) qui augmente le solde ; une méthode retirer(self, montant) qui diminue le solde si possible. Créer ensuite le compte de « Ada » avec un solde initial de 100, déposer 50, retirer 30, puis donner le solde final.

  1. 01Définir la classe et le constructeur

    On écrit « class CompteBancaire: » puis « def __init__(self, titulaire, solde_initial): » qui crée self.titulaire = titulaire et self.solde = solde_initial. self reçoit automatiquement le nouvel objet.

  2. 02Ajouter les méthodes

    deposer(self, montant) fait self.solde = self.solde + montant. retirer(self, montant) ne diminue le solde que si montant <= self.solde (sinon il refuse l'opération). Chaque méthode agit sur l'attribut via self.

  3. 03Instancier puis appeler les méthodes

    c = CompteBancaire('Ada', 100) crée l'objet (solde 100). c.deposer(50) porte le solde à 150. c.retirer(30) le ramène à 120 (30 <= 150, l'opération est autorisée).

  4. 04Lire l'état final

    On lit l'attribut de l'objet : c.solde vaut 120. La classe (moule) a produit un objet c (exemplaire) dont les méthodes ont fait évoluer l'état.

Résultat : Après c = CompteBancaire('Ada', 100), c.deposer(50) puis c.retirer(30), l'attribut c.solde vaut 120. La classe est le moule, c est l'instance, deposer/retirer sont des méthodes qui modifient l'attribut solde via self.

Objectif Bac

  • Objectif Bac : ÉCRIRE une classe complète à partir d'un énoncé — choisir des attributs pertinents, écrire le constructeur \_\_init\_\_ qui les initialise, et définir des méthodes qui agissent sur self (lecture, modification, affichage).
  • Objectif Bac : maîtriser le vocabulaire et savoir l'utiliser à bon escient dans une justification — distinguer classe et instance, attribut et méthode, et expliquer le rôle de \_\_init\_\_ et de self sur un exemple.

Erreurs fréquentes

  • Oublier self : écrire « def deposer(montant): » au lieu de « def deposer(self, montant): », ou écrire « solde = solde + montant » au lieu de « self.solde = self.solde + montant ». Sans self, on manipule une variable locale, pas l'attribut de l'objet, et la modification est perdue.
  • Confondre la CLASSE et l'INSTANCE : « CompteBancaire » est le moule, « c = CompteBancaire('Ada', 100) » est un objet. On appelle les méthodes sur l'objet (c.deposer(50)), pas sur la classe.
  • Croire que \_\_init\_\_ « renvoie » l'objet : \_\_init\_\_ ne fait qu'initialiser les attributs de self ; il ne doit rien renvoyer (return None implicite). C'est l'appel NomClasse(...) qui crée l'objet et renvoie l'instance.

Révision active

Écrire en Python une classe Rectangle dotée des attributs largeur et hauteur, d'un constructeur \_\_init\_\_(self, largeur, hauteur) qui les initialise, et de deux méthodes aire(self) et perimetre(self) qui renvoient respectivement l'aire et le périmètre. Créer ensuite un rectangle 4 × 3 et vérifier que aire() vaut 12 et perimetre() vaut 14.

Rappel actif

Rappelle-toi les points clés — puis révèle.

Sources : Programme de spécialité NSI — classe terminale (voie générale), BO spécial n° 8 du 25 juillet 2019 (Ministère de l'Éducation nationale) · Numérique et sciences informatiques — programmes et ressources, voie générale (Éduscol) (Éduscol — Ministère de l'Éducation nationale)

§ 03

Instancier et utiliser des objets en Python#

●●○StandardLPeduscol-programme-nsi-terminale

Un moule, plusieurs instances : des objets distincts

Un moule (la classe), plusieurs instances distinctesGraphe, Point (le moule : x, y) → a = Point(0, 0), Point (le moule : x, y) → b = Point(3, 4), Point (le moule : x, y) → c = Point(3, 4)Point (le moule: x, y)a = Point(0, 0)b = Point(3, 4)c = Point(3, 4)instancierinstancierinstancier
Fig. 5La classe (mise en évidence) est un moule ; chaque instanciation produit un objet distinct avec son propre état. b et c ont les mêmes valeurs (3, 4) mais sont deux objets différents (b is c vaut Faux).

Points clés

INSTANCIER, c'est créer un objet à partir d'une classe en appelant la classe comme une fonction : « b = Point(3, 4) ». Cet appel déclenche \_\_init\_\_ (avec self lié au nouvel objet), initialise les attributs, puis renvoie l'instance, que l'on range ici dans la variable b. Une même classe peut produire AUTANT d'instances que l'on veut, chacune avec son propre état.
ACCÉDER À UN ATTRIBUT se fait avec la notation pointée objet.attribut, SANS parenthèses : « b.x » lit (ou « b.x = 5 » modifie) la donnée x de l'objet b. C'est une valeur, pas une action.
APPELER UNE MÉTHODE se fait avec objet.methode(arguments), AVEC parenthèses : « b.norme() ». Les parenthèses déclenchent l'exécution ; Python passe automatiquement l'objet b comme premier paramètre self de la méthode. Ainsi b.norme() exécute norme(self) avec self = b.
Deux instances issues du même moule sont des objets DISTINCTS, même si leurs attributs ont les mêmes valeurs : « b = Point(3,4) » et « c = Point(3,4) » occupent deux emplacements mémoire différents. Modifier b.x ne change pas c.x. Chaque objet possède son propre jeu d'attributs (son état).
Distinction décisive à l'usage : un ATTRIBUT s'écrit SANS parenthèses (b.x — une donnée), une MÉTHODE s'écrit AVEC parenthèses (b.norme() — une action). Oublier les parenthèses sur une méthode ne provoque pas d'erreur immédiate : Python renvoie alors l'objet « fonction » sans l'exécuter, ce qui est un piège fréquent.
b.norme() ≡ Point.norme(b)⇒self=b\texttt{b.norme()}\ \equiv\ \texttt{Point.norme(b)} \quad\Rightarrow\quad \texttt{self} = \texttt{b}b.norme() ≡ Point.norme(b)⇒self=b

Appeler une méthode : la liaison automatique de self

La notation pointée b.norme() est équivalente à appeler la fonction de la classe en lui passant b comme premier argument self. C'est pourquoi on n'écrit jamais soi-même b.norme(b).

norme(b)=b.x2+b.y2=32+42=25=5\text{norme}(b) = \sqrt{b.x^{2} + b.y^{2}} = \sqrt{3^{2} + 4^{2}} = \sqrt{25} = 5norme(b)=b.x2+b.y2​=32+42​=25​=5

Exemple de calcul réalisé par une méthode (point (3 ; 4))

La méthode norme lit les attributs x et y de l'objet et renvoie la distance à l'origine. Pour b = Point(3, 4), elle vaut 5.

Attribut sans parenthèses, méthode avec parenthèses

Attribut (lecture) contre méthode (appel)Tableau de 3 colonnes et 2 lignes, Données: Accès · Syntaxe · Effet; attribut · b.x · lit la donnée stockée (3), sans parenthèses; méthode · b.norme() · exécute le code et renvoie un résultat (5.0), cellule mise en évidence : b.norme()ACCÈSSYNTAXEEFFETattributb.xlit la donnée stockée (3),sans parenthèsesméthodeb.norme()exécute le code et renvoieun résultat (5.0)
Fig. 6Un attribut se LIT (pas de parenthèses) ; une méthode s'APPELLE (parenthèses), elle exécute du code et renvoie un résultat. L'appel de méthode (mis en évidence) équivaut à Point.norme(b).
Exemple corrigé

Utiliser des objets Point : attributs et méthodes

La classe Point(x, y) possède une méthode norme(self) renvoyant la distance à l'origine. (a) Instancier b = Point(3, 4) et c = Point(6, 8). (b) Calculer b.norme() et c.norme(). (c) Donner b.x. (d) Après b.x = 0, la valeur de c.x change-t-elle ?

  1. 01(a) Instancier

    b = Point(3, 4) crée un objet d'attributs b.x = 3 et b.y = 4. c = Point(6, 8) crée un AUTRE objet d'attributs c.x = 6 et c.y = 8. Deux instances indépendantes.

  2. 02(b) Appeler la méthode norme

    b.norme() exécute norme(self) avec self = b : racine de 3^2 + 4^2 = 25, soit 5. c.norme() avec self = c : racine de 6^2 + 8^2 = 100, soit 10.

  3. 03(c) Lire un attribut

    b.x est un accès en lecture à la donnée x de l'objet b : il vaut 3. Pas de parenthèses : c'est une valeur, pas un appel.

  4. 04(d) Indépendance des instances

    b et c sont deux objets distincts, chacun avec son propre attribut x. L'affectation b.x = 0 ne modifie que l'objet b ; c.x reste égal à 6.

Résultat : (a) deux objets indépendants. (b) b.norme() = 5 et c.norme() = 10. (c) b.x = 3. (d) Non : c.x reste 6, car b et c sont des instances distinctes possédant chacune leur propre état.

Objectif Bac

  • Objectif Bac : à partir d'une classe fournie, INSTANCIER un ou plusieurs objets, LIRE et MODIFIER leurs attributs, et APPELER leurs méthodes avec la bonne syntaxe pointée (parenthèses pour les méthodes, pas pour les attributs).
  • Objectif Bac : prédire l'effet d'une suite d'instructions sur l'état de plusieurs objets — notamment comprendre que deux instances sont indépendantes et que modifier l'une ne touche pas l'autre.

Erreurs fréquentes

  • Confondre attribut et méthode dans la syntaxe : écrire b.norme (sans parenthèses) attendu pour obtenir une valeur ne calcule rien — Python renvoie la méthode elle-même ; il faut b.norme(). Inversement, b.x() (avec parenthèses sur un attribut) provoque une erreur car x n'est pas appelable.
  • Croire que deux instances aux mêmes valeurs sont « le même objet » : b = Point(3,4) et c = Point(3,4) sont deux objets distincts ; b == c peut être faux et b.x = 9 ne modifie pas c.x.
  • Passer soi-même self lors de l'appel : on écrit b.norme(), pas b.norme(b) ni Point.norme() — Python relie automatiquement self à l'objet placé avant le point.

Révision active

On dispose d'une classe Point(x, y) munie d'une méthode norme(self) qui renvoie la distance à l'origine. (a) Instancier deux points b = Point(3, 4) et c = Point(6, 8). (b) Donner la valeur de b.norme() et de c.norme(). (c) Que renvoie b.x ? (d) Après l'instruction b.x = 0, la valeur de c.x est-elle modifiée ? Justifier.

Rappel actif

Rappelle-toi les points clés — puis révèle.

Sources : Programme de spécialité NSI — classe terminale (voie générale), BO spécial n° 8 du 25 juillet 2019 (Ministère de l'Éducation nationale) · Numérique et sciences informatiques — programmes et ressources, voie générale (Éduscol) (Éduscol — Ministère de l'Éducation nationale)

§ 04

Spécifier une fonction : préconditions, postconditions, documentation#

●●○StandardLPeduscol-programme-nsi-terminale

Le contrat d'une fonction : précondition, fonction, postcondition

Le contrat d'une fonction : pré → fonction → postGraphe, Précondition : x ≥ 0 (à la charge de l'appelant) → Fonction racine(x), Fonction racine(x) → Postcondition : r ≥ 0 et r² = xPrécondition : x≥ 0 (à la chargede l'appelant)Fonctionracine(x)Postcondition :r ≥ 0 et r² = xsi vraiealors garantit
Fig. 7Le contrat d'une fonction : si l'appelant garantit la précondition (en entrée), alors la fonction (mise en évidence) garantit la postcondition (en sortie). Spécifier, c'est écrire ce contrat avant de coder.

Points clés

SPÉCIFIER une fonction, c'est décrire son CONTRAT : ce qu'elle prend en entrée, ce qu'elle renvoie, et sous quelles hypothèses — indépendamment de la façon dont elle est codée. La spécification dit CE QUE fait la fonction ; le code dit COMMENT. On peut tester ou raisonner sur une fonction à partir de sa seule spécification.
Une PRÉCONDITION est une propriété qui doit être VRAIE AU MOMENT DE L'APPEL pour que la fonction fonctionne correctement (ex. pour racine(x), la précondition « x >= 0 »). C'est à l'APPELANT de la garantir. Si une précondition n'est pas respectée, la fonction n'est pas tenue de donner un résultat correct.
Une POSTCONDITION est une propriété GARANTIE EN SORTIE, à condition que les préconditions aient été respectées (ex. pour racine(x) : le résultat r vérifie r >= 0 et r*r = x). C'est à la FONCTION de l'assurer. Préconditions et postconditions forment ensemble le contrat : « si tu me donnes des entrées valides, je te rends un résultat conforme ».
DOCUMENTER une fonction en Python se fait avec un DOCSTRING : une chaîne placée juste sous l'en-tête def, qui décrit le rôle de la fonction, le sens et le type de ses paramètres, la valeur renvoyée, et ses préconditions/postconditions. Le docstring est lisible par help() et fait partie du code : il en constitue la spécification écrite.
Une bonne spécification est PRÉCISE et VÉRIFIABLE : elle nomme les variables, donne leur type et leurs contraintes, et formule la postcondition comme une propriété qu'on pourrait tester (ex. par une assertion). Spécifier AVANT de coder aide à concevoir les bons tests et clarifie le comportement attendu, y compris dans les cas limites.
x≥0⏟preˊcondition (appelant)  ⟶  r = racine(x)  ⟶  r≥0 et r2=x⏟postcondition (fonction)\underbrace{x \ge 0}_{\text{précondition (appelant)}} \;\longrightarrow\; \texttt{r = racine(x)} \;\longrightarrow\; \underbrace{r \ge 0 \ \text{et}\ r^{2} = x}_{\text{postcondition (fonction)}}preˊcondition (appelant)x≥0​​⟶r = racine(x)⟶postcondition (fonction)r≥0 et r2=x​​

Le contrat d'une fonction : précondition → fonction → postcondition

Si l'appelant garantit la précondition x >= 0, la fonction garantit en retour la postcondition « r positif et r au carré égal à x ». Hors précondition, aucune garantie.

Exemple corrigé

Spécifier et documenter une fonction de division entière

On veut une fonction quotient_reste(a, b) qui renvoie le couple (q, r) du quotient et du reste de la division euclidienne de a par b, pour a et b entiers. (a) Donner la précondition. (b) Donner la postcondition liant a, b, q et r. (c) Rédiger un docstring Python conforme.

  1. 01(a) Précondition

    La division euclidienne n'a de sens que si le diviseur n'est pas nul. On impose donc, à la charge de l'appelant : a et b entiers, et b différent de 0 (on prendra ici b > 0 pour fixer le signe du reste).

  2. 02(b) Postcondition

    La fonction garantit que le couple renvoyé reconstitue a et que le reste est dans le bon intervalle : a = b*q + r avec 0 <= r < b. C'est une propriété vérifiable (testable par assertion).

  3. 03(c) Docstring

    Sous l'en-tête def quotient_reste(a, b):, on place une chaîne triple décrivant le QUOI : « Renvoie (q, r) tels que a = b*q + r et 0 <= r < b. Precondition : a entier, b entier strictement positif. » Le docstring est la spécification écrite, lisible par help().

  4. 04Vérification sur un exemple

    Pour a = 17, b = 5 : q = 3, r = 2. On vérifie la postcondition : 5*3 + 2 = 17 et 0 <= 2 < 5. Le contrat est tenu.

Résultat : Précondition : a, b entiers avec b > 0. Postcondition : a = b*q + r et 0 <= r < b. Le docstring décrit le résultat (le QUOI) et les hypothèses, sans détailler l'algorithme. Exemple : quotient_reste(17, 5) = (3, 2), conforme au contrat.

Objectif Bac

  • Objectif Bac : à partir d'un énoncé, RÉDIGER la spécification d'une fonction — préconditions sur les entrées, postcondition sur la sortie — et la traduire en un docstring clair.
  • Objectif Bac : à partir d'un docstring/spécification donné, JUSTIFIER si un appel est valide (préconditions respectées) et dire ce qui est garanti en retour (postcondition), ou repérer un appel qui viole le contrat.

Erreurs fréquentes

  • Confondre PRÉCONDITION (à la charge de l'appelant, vérifiée à l'entrée) et POSTCONDITION (à la charge de la fonction, vraie en sortie). Mettre « le résultat est positif » en précondition n'a pas de sens : c'est une postcondition.
  • Spécifier le COMMENT au lieu du QUOI : un docstring du type « on fait une boucle qui additionne » décrit l'implémentation. La spécification doit décrire le résultat (ex. « renvoie la somme des éléments de la liste L »), pas l'algorithme.
  • Oublier les cas limites dans la spécification : préciser le comportement sur l'entrée vide, sur 0, sur une liste à un élément, etc. Une postcondition silencieuse sur ces cas mène à des bugs et à des tests incomplets.

Révision active

Spécifier puis documenter (par un docstring) une fonction indice_max(L) qui renvoie l'indice d'un plus grand élément d'une liste de nombres L. Préciser la précondition (que faut-il imposer à L pour que la question ait un sens ?), la postcondition (quelle propriété l'indice renvoyé vérifie-t-il ?), et le comportement choisi en cas d'égalité entre plusieurs maximums.

Rappel actif

Rappelle-toi les points clés — puis révèle.

Sources : Programme de spécialité NSI — classe terminale (voie générale), BO spécial n° 8 du 25 juillet 2019 (Ministère de l'Éducation nationale) · Numérique et sciences informatiques — programmes et ressources, voie générale (Éduscol) (Éduscol — Ministère de l'Éducation nationale)

§ 05

Mettre au point : jeux de tests, assertions, exceptions#

●●●ApprofondissementLPeduscol-programme-nsi-terminale

Le cycle de mise au point : spécifier, coder, tester, corriger

Le cycle de mise au pointGraphe, Spécifier → Coder, Coder → Tester, Tester → Corriger, Corriger → SpécifierSpécifierCoderTesterCorriger
Fig. 8Mettre au point un programme est un cycle : spécifier, coder, tester (mis en évidence), corriger — et l'on boucle entre tester et corriger jusqu'à ce que tous les tests passent.

Points clés

METTRE AU POINT un programme, c'est l'éprouver pour repérer et corriger ses erreurs. Le cycle attendu est : SPÉCIFIER (le contrat) → CODER → TESTER → CORRIGER, en bouclant tester/corriger jusqu'à ce que tous les tests passent. Tester ne prouve pas l'absence de bugs, mais des tests bien choisis donnent une forte confiance dans le comportement.
Un JEU DE TESTS est un ensemble de cas (entrée → sortie attendue) qui documente et vérifie le comportement. Un bon jeu de tests couvre TROIS familles : les cas NOMINAUX (situations courantes), les cas LIMITES (frontières : liste vide, n = 0, n = 1, valeur minimale ou maximale) et les cas D'ERREUR (entrées invalides au regard de la précondition). Les cas limites et d'erreur sont les plus révélateurs de bugs.
Une ASSERTION (instruction assert en Python) vérifie qu'une propriété est vraie à un point du programme : « assert condition, message ». Si la condition est vraie, l'exécution continue ; si elle est fausse, Python lève une AssertionError et arrête le programme. Les assertions servent à tester (postconditions, invariants) et à détecter tôt une violation de contrat — pas à valider des entrées utilisateur en production.
Une EXCEPTION est un événement qui interrompt le cours normal du programme lorsqu'une erreur survient (division par zéro → ZeroDivisionError, indice hors limites → IndexError, valeur invalide → ValueError…). On peut la PROVOQUER volontairement avec raise (ex. raise ValueError("x doit être positif")) pour signaler une précondition violée.
GÉRER une exception se fait avec le bloc try / except : on place le code « à risque » dans try, et le code de récupération dans except TypeDException. Si l'exception visée survient, elle est RATTRAPÉE et l'on exécute le bloc except au lieu de planter. Cela permet de réagir proprement à une erreur prévue (afficher un message, redemander une saisie) plutôt que d'interrompre brutalement le programme.
PROTOTYPER puis ÉPROUVER : on écrit une première version d'une fonction, on la confronte à un jeu de tests pertinent (nominal, limite, erreur), on observe les échecs, on localise le bug, on corrige, et on relance les tests. Garder ses tests permet de vérifier qu'une correction ne casse rien d'autre (non-régression).
assert f(e) == s : {condition vraie⇒on continuecondition fausse⇒AssertionError (arreˆt)\texttt{assert f(e) == s}\ :\ \begin{cases} \text{condition vraie} & \Rightarrow \text{on continue} \\ \text{condition fausse} & \Rightarrow \text{AssertionError (arrêt)} \end{cases}assert f(e) == s : {condition vraiecondition fausse​⇒on continue⇒AssertionError (arreˆt)​

Sémantique d'une assertion de test

Une assertion compare le résultat f(e) au résultat attendu s : si l'égalité est vraie le programme poursuit, sinon il s'arrête en signalant l'échec — exactement ce qu'on veut pendant la mise au point.

Un bon jeu de tests : nominal, limite, erreur

Un bon jeu de tests : nominal, limite, erreurTableau de 3 colonnes et 3 lignes, Données: Type de cas · Exemple · Ce qu'on vérifie; nominal · racine(9) == 3 · le comportement courant; limite · racine(0) == 0 · les frontières : 0, 1, liste vide; erreur · racine(-1) lève une erreur · le refus des entrées invalides, cellule mise en évidence : limiteTYPE DE CASEXEMPLECE QU'ON VÉRIFIEnominalracine(9) == 3le comportement courantlimiteracine(0) == 0les frontières : 0, 1, listevideerreurracine(-1) lève une erreurle refus des entréesinvalides
Fig. 9Un jeu de tests couvre trois familles de cas : nominal (l'usage courant), limite (les frontières, mises en évidence : 0, 1, liste vide) et erreur (les entrées invalides, qui doivent être refusées).

Assertion qui échoue et rattrapage d'exception (try/except)

assert (hypothèse) contre try/except (rattrapage)Tableau de 3 colonnes et 2 lignes, Données: Mécanisme · Quand l'utiliser · Effet en cas de problème; assert (condition) · vérifier une hypothèse interne (mise au point) · lève AssertionError et arrête le programme; try / except · rattraper une erreur prévue à l'exécution · exécute le bloc except ; le programme continue, cellule mise en évidence : exécute le bloc except ; le programme continueMÉCANISMEQUAND L'UTILISEREFFET EN CAS DEPROBLÈMEassert (condition)vérifier une hypothèseinterne (mise au point)lève AssertionError etarrête le programmetry / exceptrattraper une erreur prévueà l'exécutionexécute le bloc except ; leprogramme continue
Fig. 10Deux mécanismes complémentaires. assert vérifie une hypothèse interne (mise au point) et arrête le programme si elle est fausse. try/except (mis en évidence) rattrape une erreur prévue à l'exécution pour que le programme continue proprement.
Exemple corrigé

Mettre au point une fonction racine_entiere(n)

On veut racine_entiere(n) qui, pour un entier n >= 0, renvoie le plus grand entier r tel que r*r <= n (partie entière de la racine carrée). (a) Donner la spécification (pré/postcondition). (b) Écrire un jeu de tests par assertions (nominal, limite, erreur). (c) Une première version oublie le cas n = 0 et tombe en boucle infinie ; expliquer comment le jeu de tests le révèle et comment lever une exception sur une entrée négative.

  1. 01(a) Spécifier le contrat

    Précondition (appelant) : n est un entier et n >= 0. Postcondition (fonction) : le résultat r vérifie rr <= n < (r+1)(r+1), c'est-à-dire r est la partie entière de la racine de n.

  2. 02(b) Cas nominaux (assertions)

    racine_entiere(9) doit valoir 3 (3^2 = 9 <= 9 < 16), racine_entiere(10) doit valoir 3 (9 <= 10 < 16), racine_entiere(15) doit valoir 3, racine_entiere(16) doit valoir 4.

  3. 03(b) Cas limites et cas d'erreur

    Limites : racine_entiere(0) == 0 et racine_entiere(1) == 1 (frontières). Erreur : racine_entiere(-4) viole la précondition ; on attend qu'elle LÈVE une exception, ce qu'on teste avec try/except.

  4. 04(c) Le test révèle le bug, puis on corrige

    Le cas limite racine_entiere(0) fait tourner la boucle infinie de la première version : l'assertion ne renvoie jamais de résultat, le bug est localisé sur n = 0. On corrige (initialisation/condition d'arrêt) et on protège la précondition : « if n < 0: raise ValueError('n doit etre >= 0') », qu'on rattrape par try/except au point d'appel.

Résultat : La spécification fixe r tel que r^2 <= n < (r+1)^2 pour n >= 0. Le jeu de tests nominal/limite/erreur — notamment le cas limite n = 0 — fait apparaître la boucle infinie de la première version et la localise ; on corrige la condition d'arrêt, puis on lève une ValueError sur n < 0, rattrapée par un bloc try/except. Les tests conservés assurent la non-régression.

Objectif Bac

  • Objectif Bac : CONCEVOIR un jeu de tests pertinent pour une fonction donnée — proposer des cas nominaux, limites et d'erreur, et écrire les assertions correspondantes (assert f(entree) == sortie_attendue).
  • Objectif Bac : utiliser assert, raise et try/except à bon escient — savoir ce que fait une assertion qui échoue, lever une exception quand une précondition est violée, et écrire un bloc try/except qui rattrape une exception nommée.

Erreurs fréquentes

  • Ne tester que des cas « qui marchent ». Sans cas limites (liste vide, 0, 1 élément) ni cas d'erreur, des bugs fréquents (débordement d'indice, division par zéro) passent inaperçus. Le jeu de tests doit chercher à FAIRE ÉCHOUER le programme.
  • Confondre assert et un test conditionnel de production : « assert » est un outil de mise au point/contrôle d'invariant ; il peut être désactivé globalement et ne doit pas servir à valider des données saisies par l'utilisateur. Pour cela on lève (raise) une exception et on la gère.
  • Mettre un except trop large (« except: » nu) qui rattrape TOUTES les exceptions, y compris des erreurs imprévues, et masque ainsi de vrais bugs. On rattrape l'exception PRÉCISE attendue (except ValueError:).
  • Croire que « tous les tests passent » prouve que le programme est correct. Le test montre la présence de bugs, jamais leur absence ; il complète, sans remplacer, le raisonnement sur la spécification.

Révision active

On dispose d'une fonction moyenne(L) censée renvoyer la moyenne des éléments d'une liste de nombres. (a) Écrire un jeu de tests par assertions couvrant un cas nominal, un cas limite (liste à un seul élément) et un cas d'erreur (liste vide). (b) Proposer un comportement raisonnable sur la liste vide (lever une exception) et écrire un bloc try/except qui l'utilise sans faire planter le programme. (c) Indiquer un bug classique que ces tests permettraient de détecter.

Rappel actif

Rappelle-toi les points clés — puis révèle.

Sources : Programme de spécialité NSI — classe terminale (voie générale), BO spécial n° 8 du 25 juillet 2019 (Ministère de l'Éducation nationale) · Numérique et sciences informatiques — programmes et ressources, voie générale (Éduscol) (Éduscol — Ministère de l'Éducation nationale)

Sommaire

Section -- / 05

    • 01Paradigmes de programmation : impératif, fonctionnel, objet○
    • 02Programmation orientée objet : classes, attributs, méthodes◐
    • 03Instancier et utiliser des objets en Python◐
    • 04Spécifier une fonction : préconditions, postconditions, documentation◐
    • 05Mettre au point : jeux de tests, assertions, exceptions●

0/5 Lues

Des fiches à l'entraînement

Langages et paradigmes de programmation

Consolide ce thème avec des questions de la banque de questions.

~27
min
4
Compétences
S'entraîner

Références et sources

Sources

Ministère de l'Éducation nationale

  • Programme de spécialité NSI — classe terminale (voie générale), BO spécial n° 8 du 25 juillet 2019

Éduscol — Ministère de l'Éducation nationale

  • Numérique et sciences informatiques — programmes et ressources, voie générale (Éduscol)

Chapitre précédent

Récursivité, calculabilité et décidabilité

Chapitre suivant

Diviser pour régner et programmation dynamique

EuraStudy·Fiches T·05·MMXXVI

Continuez avec le chapitre suivant — le parcours est conservé.