Modularité et Mise au Point

Langages et Programmation

🦊

🧩 Modularité et Mise au Point

En Terminale NSI, il ne s'agit plus seulement d'écrire du code qui "marche", mais d'écrire du code robuste, maintenable et vérifié. Ce chapitre aborde les bonnes pratiques pour structurer vos programmes et chasser les bugs efficacement.


1. La Modularité

La modularité consiste à découper un programme complexe en plusieurs fichiers ou composants plus petits et indépendants, appelés modules.

Pourquoi modulariser ?

  • Organisation : Le code est plus lisible et plus facile à naviguer.
  • Réutilisabilité : Une fonction bien écrite dans un module peut être utilisée dans plusieurs projets (ex: le module math).
  • Collaboration : Plusieurs développeurs peuvent travailler sur des modules différents sans se gêner.

Créer et importer un module en Python

Un module Python est simplement un fichier .py.

Exemple : Imaginons un fichier geometrie.py :

# Fichier: geometrie.py
PI = 3.14159

def aire_cercle(rayon):
    """Calcule l'aire d'un cercle."""
    return PI * rayon * rayon

def perimetre_cercle(rayon):
    return 2 * PI * rayon

Pour l'utiliser dans un autre fichier main.py :

# Fichier: main.py
import geometrie

r = 5
aire = geometrie.aire_cercle(r)
print(f"L'aire est {aire}")
Variantes d'import

Il existe plusieurs façons d'importer. Que font les commandes suivantes ?

  1. import geometrie
  2. from geometrie import aire_cercle
  3. from geometrie import *
  4. import geometrie as geo

2. Gestion des Bugs et Exceptions

Un bug est une erreur dans un programme informatique. On distingue trois types d'erreurs principales :

  1. Erreurs de syntaxe : Le code ne respecte pas les règles du langage (ex: oubli de : après un if). Python arrête l'exécution immédiatement.
  2. Erreurs d'exécution (Exceptions) : Le code est syntaxiquement correct, mais une opération impossible est demandée pendant l'exécution (ex: division par zéro, index hors liste).
  3. Erreurs de logique : Le programme ne plante pas, mais le résultat est faux (ex: formule mathématique incorrecte). Ce sont les plus difficiles à détecter !

Gérer les exceptions avec try / except

Pour éviter qu'un programme ne plante brutalement, on peut "attraper" les exceptions.

def division_sur(a, b):
    try:
        resultat = a / b
        return resultat
    except ZeroDivisionError:
        print("Erreur : Division par zéro impossible !")
        return None
    except TypeError:
        print("Erreur : Les arguments doivent être des nombres.")
        return None

3. Programmation Défensive : assert

La programmation défensive consiste à anticiper les problèmes. L'instruction assert permet de vérifier une condition en cours d'exécution. Si la condition est fausse, le programme s'arrête avec une erreur (AssertionError).

C'est très utile pour vérifier les préconditions d'une fonction (ce que la fonction attend en entrée).

def calcul_moyenne(notes):
    # Précondition : la liste ne doit pas être vide
    assert len(notes) > 0, "La liste de notes ne doit pas être vide"
    
    somme = 0
    for note in notes:
        # Invariant : chaque note doit être valide
        assert 0 <= note <= 20, f"Note invalide trouvée : {note}"
        somme += note
    return somme / len(notes)
Pratique

Écrivez une fonction racine_carree(x) qui utilise math.sqrt. Ajoutez une assertion pour vérifier que x est positif ou nul.


4. Tests Unitaires et Doctests

Pour vérifier qu'une fonction fait bien ce qu'elle est censée faire (pas d'erreur de logique), on écrit des tests.

Les Doctests

C'est une spécificité géniale de Python : on écrit les tests directement dans la documentation de la fonction (docstring). Ils servent à la fois d'exemple pour l'utilisateur et de test automatique.

def somme(a, b):
    """
    Calcule la somme de deux nombres.

    Exemples :
    >>> somme(2, 3)
    5
    >>> somme(-1, 1)
    0
    >>> somme(0, 0)
    0
    """
    return a + b

if __name__ == "__main__":
    import doctest
    doctest.testmod()

Si tout va bien, doctest.testmod() n'affiche rien. S'il y a une erreur, il l'affiche en détail.

Les Tests Unitaires (unittest)

Pour des projets plus gros, on utilise le module unittest. On crée une classe qui hérite de unittest.TestCase.

import unittest

def multiplication(a, b):
    return a * b

class TestCalculs(unittest.TestCase):
    def test_multiplication_simple(self):
        self.assertEqual(multiplication(3, 4), 12)

    def test_multiplication_zero(self):
        self.assertEqual(multiplication(10, 0), 0)

if __name__ == '__main__':
    unittest.main()
Écrire un doctest

Ajoutez des doctests à la fonction suivante qui vérifie si un nombre est pair.

def est_pair(n):
    return n % 2 == 0

Testez avec 4 (True), 5 (False) et 0 (True).


📝 Résumé

ConceptDescriptionOutil Python
ModularitéDécouper le code en fichiers indépendantsimport, from ... import
ExceptionGérer les erreurs d'exécution proprementtry ... except
AssertionVérifier les conditions en cours de développementassert condition, "message"
DoctestTests simples intégrés à la documentationimport doctest, >>> dans docstring
Test UnitaireTests complets et structurésimport unittest