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 outilspuisoutils.carre(5) - C)
import carre from outils - D)
outils.carre(5)directement, sans import
Correction
Réponse B. L’instruction import outils rend le module accessible, puis outils.carre(5) appelle la fonction avec la notation pointée. On peut aussi écrire from outils import carre pour utiliser carre(5) directement.
- A est faux :
includen’existe pas en Python (c’est un mot-clé du C) ; - C est faux : la syntaxe correcte est
from outils import carre(pasimport ... from) ; - D est faux : sans
import, Python ne sait pas où trouveroutils.
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
Réponse B. La docstring est une chaîne (généralement entre triples guillemets """...""") placée comme première instruction d’une fonction, classe ou module. Elle est accessible via help() ou l’attribut __doc__.
- A est faux : les commentaires
#sont ignorés par Python et ne sont pas des docstrings ; - C est faux : la docstring est dans le code Python, pas dans un fichier séparé ;
- D est faux : le nom du module est dans
__name__, pas dans la docstring.
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
Réponse B. doctest lit les exemples au format >>> ... dans les docstrings et les exécute pour vérifier que la sortie correspond au résultat indiqué. C’est un moyen simple d’écrire des tests intégrés à la documentation.
- A est faux :
doctestne génère pas de documentation, il teste les exemples existants ; - C est faux : les exemples de
doctestsont dans les docstrings, pas dans un fichier séparé (contrairement àunittest) ; - D est faux :
doctestsignale les erreurs mais ne les corrige pas.
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 écritmath.sqrt(4); avecfrom math import sqrt, on écritsqrt(4) - C)
from math import sqrtest plus lent car il charge tout le module - D)
import mathn’importe que la fonctionmath, pas le module entier
Correction
Réponse B. import math importe le module entier : on accède aux fonctions par math.sqrt, math.pi, etc. from math import sqrt n’importe que sqrt dans l’espace de noms courant, ce qui permet d’écrire sqrt(4) directement.
- A est faux : l’accès aux fonctions diffère (notation pointée ou non) ;
- C est faux : dans les deux cas, Python charge le module en mémoire ;
- D est faux :
import mathimporte bien le modulemath.
Question 5. Un paquet (package) Python est :
- A) un fichier
.pycontenant plusieurs fonctions - B) un répertoire contenant un fichier
__init__.pyet un ou plusieurs modules - C) un fichier compressé
.zipcontenant du code Python - D) une bibliothèque externe installée avec
pip
Correction
Réponse B. Un paquet est un répertoire qui contient un fichier __init__.py (éventuellement vide) et des modules .py. Cela permet d’organiser un projet en sous-modules avec une importation hiérarchique comme from mon_paquet.module import fonction.
- A est faux : c’est la définition d’un module, pas d’un paquet ;
- C est faux : un
.zippeut contenir du code, mais ce n’est pas la définition d’un paquet ; - D est faux :
pipinstalle des paquets, mais un paquet est défini par sa structure de répertoire.
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).
Écrire un programme qui importe le module
geometrieet affiche l’aire et le périmètre d’un cercle de rayon 7.Réécrire le même programme en n’important que les fonctions
aire_cercleetperimetre_cercle. Quelle syntaxe faut-il utiliser ?Que se passe-t-il si on écrit
from geometrie import *? Pourquoi cette pratique est-elle déconseillée ?Écrire l’instruction permettant d’afficher la documentation de la fonction
aire_cercledepuis l’interpréteur Python.
Correction
- Avec
import:
import geometrie
print(geometrie.aire_cercle(7)) # 153.93804002589985
print(geometrie.perimetre_cercle(7)) # 43.982297150257104
- Avec
from ... import:
from geometrie import aire_cercle, perimetre_cercle
print(aire_cercle(7)) # 153.93804002589985
print(perimetre_cercle(7)) # 43.982297150257104
La syntaxe from module import fonction1, fonction2 permet d’importer uniquement les fonctions nécessaires et de les utiliser sans préfixe.
from geometrie import *importe toutes les fonctions du module dans l’espace de noms courant. C’est déconseillé car cela peut provoquer des conflits de noms : si deux modules définissent une fonctionaire, la seconde importation écrase la première silencieusement. De plus, on ne sait plus d’où vient chaque fonction en lisant le code.Pour afficher la documentation :
import geometrie
help(geometrie.aire_cercle)
Cela affiche la docstring de la fonction, qui décrit son rôle et donne des exemples.
Exercice 3. Écrire et tester des docstrings (guidé)
- 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]
É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 exemplepgcd(0, 5)).Écrire le code permettant de lancer les tests
doctestsur ces deux fonctions.
Correction
- Docstring complétée :
def est_palindrome(mot):
"""Renvoie True si mot est un palindrome, False sinon.
>>> est_palindrome("kayak")
True
>>> est_palindrome("python")
False
"""
return mot == mot[::-1]
- Fonction
pgcddocumentée :
def pgcd(a, b):
"""Renvoie le PGCD de a et b par l'algorithme d'Euclide.
>>> pgcd(48, 18)
6
>>> pgcd(17, 5)
1
>>> pgcd(0, 5)
5
"""
while b != 0:
a, b = b, a % b
return a
Pour pgcd(0, 5) : la boucle s’exécute une seule fois (a, b devient 5, 0), puis on renvoie 5. C’est cohérent : tout entier divise 0, donc le PGCD de 0 et 5 est 5.
- Lancement des tests :
import doctest
doctest.testmod()
Si les fonctions sont dans un module mes_fonctions.py, on peut aussi lancer depuis le terminal :
python3 -m doctest mes_fonctions.py -v
L’option -v (verbose) affiche le détail de chaque test, même ceux qui réussissent.
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 :
moyenne(liste): retourne la moyenne arithmétique d’une liste de nombres.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.ecart_type(liste): retourne l’écart type (racine carrée de la variance).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.Ajouter en fin de fichier le bloc suivant et expliquer son rôle :
if __name__ == "__main__":
import doctest
doctest.testmod()
Correction
"""Module de calculs statistiques de base."""
import math
def moyenne(liste):
"""Renvoie la moyenne arithmétique d'une liste de nombres.
>>> moyenne([10, 20, 30])
20.0
>>> moyenne([4])
4.0
"""
return sum(liste) / len(liste)
def variance(liste):
"""Renvoie la variance de la liste.
>>> variance([10, 20, 30])
66.66666666666667
>>> variance([5, 5, 5])
0.0
"""
m = moyenne(liste)
return sum((x - m) ** 2 for x in liste) / len(liste)
def ecart_type(liste):
"""Renvoie l'écart type de la liste.
>>> round(ecart_type([10, 20, 30]), 2)
8.16
>>> ecart_type([5, 5, 5])
0.0
"""
return math.sqrt(variance(liste))
def mediane(liste):
"""Renvoie la médiane d'une liste de nombres.
>>> mediane([3, 1, 2])
2
>>> mediane([4, 1, 3, 2])
2.5
"""
tri = sorted(liste)
n = len(tri)
if n % 2 == 1:
return tri[n // 2]
else:
return (tri[n // 2 - 1] + tri[n // 2]) / 2
if __name__ == "__main__":
import doctest
doctest.testmod()
Explication du bloc if __name__ == "__main__" : quand on exécute directement le fichier (python3 stats.py), la variable __name__ vaut "__main__" et les tests sont lancés. En revanche, quand on importe le module depuis un autre fichier (import stats), __name__ vaut "stats" et les tests ne s’exécutent pas automatiquement. Ce mécanisme permet de distinguer l’exécution directe (pour tester) de l’importation (pour utiliser).
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/.
Proposer une structure de fichiers pour ce paquet, avec les modules suivants :
eleve.py: contient une classeEleveavec un nom et une liste de notes ;calculs.py: contient les fonctionsmoyenne(notes)etmeilleure_note(notes);affichage.py: contient une fonctionbulletin(eleve)qui affiche le nom, la moyenne et la meilleure note.
Écrire le contenu du fichier
eleve.pyavec une classeElevedotée d’un constructeur (__init__), d’une méthodeajouter_note(note)et d’une méthode__repr__. Documenter chaque méthode.Écrire le contenu du fichier
calculs.pyavec les deux fonctions documentées.Écrire le contenu du fichier
affichage.py. Ce module devra importer les fonctionsmoyenneetmeilleure_notedecalculs.py.Écrire un fichier
main.py(en dehors du paquet) qui crée deux élèves, leur ajoute des notes, puis affiche leur bulletin.
Correction
- Structure du projet :
gestion_notes/
├── __init__.py
├── eleve.py
├── calculs.py
└── affichage.py
main.py
Le fichier __init__.py peut être vide. Il signale à Python que le répertoire gestion_notes est un paquet.
- Fichier
eleve.py:
"""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.
>>> e = Eleve("Alice")
>>> e.nom
'Alice'
>>> e.notes
[]
"""
self.nom = nom
self.notes = []
def ajouter_note(self, note):
"""Ajoute une note à la liste de l'élève.
>>> e = Eleve("Alice")
>>> e.ajouter_note(15)
>>> e.ajouter_note(12)
>>> e.notes
[15, 12]
"""
self.notes.append(note)
def __repr__(self):
"""Renvoie une représentation lisible de l'élève.
>>> Eleve("Alice")
Eleve('Alice', notes=[])
"""
return f"Eleve('{self.nom}', notes={self.notes})"
- Fichier
calculs.py:
"""Module de calculs sur les notes."""
def moyenne(notes):
"""Renvoie la moyenne d'une liste de notes.
>>> moyenne([12, 14, 16])
14.0
>>> moyenne([20])
20.0
"""
return sum(notes) / len(notes)
def meilleure_note(notes):
"""Renvoie la meilleure note de la liste.
>>> meilleure_note([12, 14, 16])
16
>>> meilleure_note([8])
8
"""
return max(notes)
- Fichier
affichage.py:
"""Module d'affichage des bulletins."""
from gestion_notes.calculs import moyenne, meilleure_note
def bulletin(eleve):
"""Affiche le bulletin d'un élève.
Paramètre : un objet Eleve possédant les attributs nom et notes.
"""
if not eleve.notes:
print(f"{eleve.nom} : aucune note enregistrée.")
return
moy = round(moyenne(eleve.notes), 2)
meilleure = meilleure_note(eleve.notes)
print(f"Bulletin de {eleve.nom}")
print(f" Moyenne : {moy}")
print(f" Meilleure note : {meilleure}")
- Fichier
main.py:
from gestion_notes.eleve import Eleve
from gestion_notes.affichage import bulletin
alice = Eleve("Alice")
alice.ajouter_note(15)
alice.ajouter_note(12)
alice.ajouter_note(18)
bob = Eleve("Bob")
bob.ajouter_note(9)
bob.ajouter_note(14)
bulletin(alice)
bulletin(bob)
Résultat :
Bulletin de Alice
Moyenne : 15.0
Meilleure note : 18
Bulletin de Bob
Moyenne : 11.5
Meilleure note : 14
Ce projet illustre les principes de modularité : chaque module a une responsabilité claire (données, calculs, affichage), les imports sont explicites, et les fonctions sont documentées. On peut tester chaque module indépendamment avec doctest.