Exercices : modularité

Exercice 1. QCM d’activation

Pour chaque question, une seule réponse est correcte.

Question 1. On dispose d’un fichier outils.py contenant une fonction carre(n). Pour utiliser cette fonction depuis un autre fichier Python situé dans le même répertoire, on écrit :

  • A) include outils
  • B) import outils puis outils.carre(5)
  • C) import carre from outils
  • D) outils.carre(5) directement, sans import

Correction publiée après la séance.

Question 2. Une docstring est :

  • A) un commentaire commençant par #
  • B) une chaîne de caractères placée en première ligne d’une fonction ou d’un module, servant de documentation
  • C) un fichier texte accompagnant le module
  • D) une variable spéciale contenant le nom du module

Correction publiée après la séance.

Question 3. Le module doctest permet de :

  • A) générer automatiquement la documentation d’un module
  • B) vérifier que les exemples écrits dans les docstrings produisent le résultat attendu
  • C) créer des tests unitaires dans un fichier séparé
  • D) corriger automatiquement les erreurs dans les docstrings

Correction publiée après la séance.

Question 4. Quelle est la différence entre import math et from math import sqrt ?

  • A) Il n’y a aucune différence
  • B) Avec import math, on écrit math.sqrt(4) ; avec from math import sqrt, on écrit sqrt(4)
  • C) from math import sqrt est plus lent car il charge tout le module
  • D) import math n’importe que la fonction math, pas le module entier

Correction publiée après la séance.

Question 5. Un paquet (package) Python est :

  • A) un fichier .py contenant plusieurs fonctions
  • B) un répertoire contenant un fichier __init__.py et un ou plusieurs modules
  • C) un fichier compressé .zip contenant du code Python
  • D) une bibliothèque externe installée avec pip

Correction publiée après la séance.

Exercice 2. Utiliser un module (guidé)

On dispose du module geometrie.py vu en cours, contenant les fonctions aire_cercle(rayon), perimetre_cercle(rayon) et aire_rectangle(longueur, largeur).

  1. Écrire un programme qui importe le module geometrie et affiche l’aire et le périmètre d’un cercle de rayon 7.

    Indice

    Après import geometrie, chaque fonction du module s’appelle avec le nom du module devant, en notation pointée.

  2. Réécrire le même programme en n’important que les fonctions aire_cercle et perimetre_cercle. Quelle syntaxe faut-il utiliser ?

    Indice

    Relire dans le cours la seconde forme d’importation, celle qui nomme les fonctions à importer : comment s’appellent-elles ensuite ?

  3. Que se passe-t-il si on écrit from geometrie import * ? Pourquoi cette pratique est-elle déconseillée ?

    Indice

    Que se passerait-il si le programme, ou un autre module importé de la même façon, définissait déjà une fonction du même nom ? En lisant le code, sait-on encore d’où vient chaque fonction ?

  4. Écrire l’instruction permettant d’afficher la documentation de la fonction aire_cercle depuis l’interpréteur Python.

    Indice

    La fonction help affiche la docstring de ce qu’on lui passe : un module, ou l’une de ses fonctions en notation pointée.

Correction publiée après la séance.

Exercice 3. Écrire et tester des docstrings (guidé)

  1. Compléter les docstrings de la fonction suivante en ajoutant une description et deux exemples testables avec doctest :
def est_palindrome(mot):
    """
    ___________________________________

    >>> est_palindrome("kayak")
    ____
    >>> est_palindrome("python")
    ____
    """
    return mot == mot[::-1]
Indice

La description dit en une phrase ce que renvoie la fonction. Sous chaque ligne >>>, écrire exactement ce que Python afficherait pour cet appel : ici un booléen.

  1. Écrire une fonction pgcd(a, b) qui calcule le PGCD de deux entiers par l’algorithme d’Euclide. Ajouter une docstring complète avec au moins trois exemples, dont un cas limite (par exemple pgcd(0, 5)).

    Indice

    Algorithme d’Euclide : tant que b n’est pas nul, remplacer le couple (a, b) par (b, a % b) ; le PGCD est alors la valeur de a. Pour les exemples, choisir des couples dont on connaît le PGCD, comme 48 et 18.

  2. Écrire le code permettant de lancer les tests doctest sur ces deux fonctions.

    Indice

    Le module doctest fournit la fonction testmod, qui exécute tous les exemples des docstrings du fichier : importer le module, puis appeler cette fonction.

Correction publiée après la séance.

Exercice 4. Créer un module de statistiques

On souhaite créer un module stats.py contenant les fonctions suivantes, chacune documentée avec une docstring et des exemples doctest :

  1. moyenne(liste) : retourne la moyenne arithmétique d’une liste de nombres.

    Indice

    sum(liste) donne la somme des éléments et len(liste) leur nombre. Pour les exemples, choisir une liste dont on connaît la moyenne de tête, comme [10, 20, 30].

  2. variance(liste) : retourne la variance de la liste, définie par $\frac{1}{n}\sum_{i=1}^{n}(x_i - \bar{x})^2$ où $\bar{x}$ est la moyenne.

    Indice

    Calculer d’abord la moyenne m avec la fonction précédente, puis la moyenne des carrés des écarts (x - m) ** 2.

    Code à copier puis compléter
    def variance(liste):
        """Renvoie la variance de la liste.
    
        >>> variance([5, 5, 5])
        0.0
        """
        m = moyenne(liste)
        total = 0
        for x in liste:
            total = total + ...
        return ...
    
  3. ecart_type(liste) : retourne l’écart type (racine carrée de la variance).

    Indice

    Réutiliser variance ; la racine carrée s’obtient avec math.sqrt, après import math en tête du module.

  4. mediane(liste) : retourne la médiane d’une liste triée. Si la liste a un nombre pair d’éléments, la médiane est la moyenne des deux éléments centraux.

    Indice

    Trier d’abord une copie de la liste avec sorted. Si sa longueur n est impaire, un seul élément est au milieu ; si elle est paire, il y en a deux. Tester sur [1, 2, 3] et [1, 2, 3, 4] pour trouver leurs indices en fonction de n.

    Code à copier puis compléter
    def mediane(liste):
        tri = sorted(liste)
        n = len(tri)
        if n % 2 == 1:
            return ...
        else:
            return (... + ...) / 2
    
  5. Ajouter en fin de fichier le bloc suivant et expliquer son rôle :

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

Ajouter print(__name__) dans le module, puis exécuter le fichier directement, et enfin l’importer depuis un autre fichier : quelle valeur prend __name__ dans chaque cas ?

Correction publiée après la séance.

Exercice 5. Structurer un projet (synthèse)

On souhaite organiser un petit projet de gestion de notes en trois modules, regroupés dans un paquet gestion_notes/.

  1. Proposer une structure de fichiers pour ce paquet, avec les modules suivants :

    • eleve.py : contient une classe Eleve avec un nom et une liste de notes ;
    • calculs.py : contient les fonctions moyenne(notes) et meilleure_note(notes) ;
    • affichage.py : contient une fonction bulletin(eleve) qui affiche le nom, la moyenne et la meilleure note.
    Indice

    Un paquet est un répertoire qui contient un fichier __init__.py, éventuellement vide, et les modules du projet.

  2. Écrire le contenu du fichier eleve.py avec une classe Eleve dotée d’un constructeur (__init__), d’une méthode ajouter_note(note) et d’une méthode __repr__. Documenter chaque méthode.

    Indice

    Le constructeur crée les attributs nom et notes, la liste de notes étant vide au départ. __repr__ renvoie une chaîne, par exemple Eleve('Alice', notes=[15, 12]), que Python affiche quand on évalue l’objet dans la console.

    Code à copier puis compléter
    """Module de gestion des élèves."""
    
    
    class Eleve:
        """Représente un élève avec son nom et ses notes."""
    
        def __init__(self, nom):
            """Initialise un élève avec un nom et une liste de notes vide."""
            self.nom = ...
            self.notes = ...
    
        def ajouter_note(self, note):
            """Ajoute une note à la liste de l'élève."""
            ...
    
        def __repr__(self):
            """Renvoie une représentation lisible de l'élève."""
            return f"Eleve('{...}', notes={...})"
    
  3. Écrire le contenu du fichier calculs.py avec les deux fonctions documentées.

    Indice

    Ces deux fonctions ne dépendent pas de la classe : elles reçoivent une simple liste de nombres. sum, len et max suffisent.

  4. Écrire le contenu du fichier affichage.py. Ce module devra importer les fonctions moyenne et meilleure_note de calculs.py.

    Indice

    Le programme est lancé depuis main.py, en dehors du paquet : l’import doit donc nommer le paquet, from gestion_notes.calculs import .... La fonction bulletin lit les attributs nom et notes de l’objet reçu.

  5. Écrire un fichier main.py (en dehors du paquet) qui crée deux élèves, leur ajoute des notes, puis affiche leur bulletin.

    Indice

    Importer la classe Eleve depuis gestion_notes.eleve et la fonction bulletin depuis gestion_notes.affichage, puis créer chaque élève avec Eleve("Alice") et appeler ajouter_note plusieurs fois.

Correction publiée après la séance.