Files
TermNSI/Modularité/TP_MiniPackage.md

570 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# TP : Créer son premier package Python — MathTools
> **Thème** : Modularité, documentation et tests
---
## Contexte
En 2026, Python reste le langage le plus populaire au monde grâce à son écosystème de packages. Des millions de développeurs partagent leur code via PyPI (Python Package Index).
Dans ce TP, vous allez créer votre propre **mini-package** : une bibliothèque d'outils mathématiques appelée **MathTools**. Vous apprendrez à :
- Structurer un projet en modules
- Documenter proprement avec des docstrings
- Tester automatiquement avec doctest
- Créer un point d'entrée principal
---
## Partie 1 : Structure du projet
### Organisation des fichiers
Créez la structure de dossiers suivante :
```
mathtools/
├── __init__.py
├── geometrie.py
├── statistiques.py
├── conversion.py
└── main.py
```
### Le fichier `__init__.py`
Ce fichier spécial indique à Python que le dossier est un **package**. Il peut rester vide ou contenir des imports.
```python
# mathtools/__init__.py
"""
MathTools - Une bibliothèque d'outils mathématiques.
Modules disponibles :
- geometrie : calculs géométriques (aire, périmètre, volume)
- statistiques : calculs statistiques (moyenne, médiane, écart-type)
- conversion : conversions d'unités (température, distance, poids)
"""
__version__ = "1.0.0"
__author__ = "Votre Nom"
```
---
## Partie 2 : Module géométrie
### Exercice 1 : Créer le module `geometrie.py`
Implémentez les fonctions suivantes avec leur **docstring** et **doctest** :
```python
# mathtools/geometrie.py
"""Module de calculs géométriques."""
import math
import doctest
def aire_rectangle(longueur, largeur):
"""
Calcule l'aire d'un rectangle.
:param longueur: (float) la longueur du rectangle
:param largeur: (float) la largeur du rectangle
:return: (float) l'aire du rectangle
:CU: longueur > 0 et largeur > 0
Exemple:
>>> aire_rectangle(5, 3)
15
>>> aire_rectangle(2.5, 4)
10.0
"""
# À compléter
pass
def aire_cercle(rayon):
"""
Calcule l'aire d'un cercle.
:param rayon: (float) le rayon du cercle
:return: (float) l'aire du cercle
:CU: rayon > 0
Exemple:
>>> round(aire_cercle(1), 4)
3.1416
>>> round(aire_cercle(2), 4)
12.5664
"""
# À compléter
pass
def perimetre_rectangle(longueur, largeur):
"""
Calcule le périmètre d'un rectangle.
Exemple:
>>> perimetre_rectangle(5, 3)
16
"""
# À compléter (ajoutez la docstring complète)
pass
def volume_sphere(rayon):
"""
Calcule le volume d'une sphère.
Formule : V = (4/3) * π * r³
Exemple:
>>> round(volume_sphere(1), 4)
4.1888
"""
# À compléter (ajoutez la docstring complète)
pass
def hypotenuse(a, b):
"""
Calcule l'hypoténuse d'un triangle rectangle.
Utilise le théorème de Pythagore : c² = a² + b²
Exemple:
>>> hypotenuse(3, 4)
5.0
>>> hypotenuse(5, 12)
13.0
"""
# À compléter
pass
if __name__ == "__main__":
doctest.testmod(verbose=True)
```
---
## Partie 3 : Module statistiques
### Exercice 2 : Créer le module `statistiques.py`
```python
# mathtools/statistiques.py
"""Module de calculs statistiques."""
import doctest
def moyenne(liste):
"""
Calcule la moyenne d'une liste de nombres.
:param liste: (list) une liste de nombres
:return: (float) la moyenne
:CU: liste non vide
Exemple:
>>> moyenne([10, 20, 30])
20.0
>>> moyenne([15, 18, 12, 9])
13.5
"""
# À compléter
pass
def mediane(liste):
"""
Calcule la médiane d'une liste de nombres.
La médiane est la valeur centrale d'une liste triée.
:param liste: (list) une liste de nombres
:return: (float) la médiane
:CU: liste non vide
Exemple:
>>> mediane([1, 2, 3, 4, 5])
3
>>> mediane([1, 2, 3, 4])
2.5
"""
# À compléter
pass
def variance(liste):
"""
Calcule la variance d'une liste de nombres.
Variance = moyenne des carrés des écarts à la moyenne.
Exemple:
>>> variance([2, 4, 4, 4, 5, 5, 7, 9])
4.0
"""
# À compléter
pass
def ecart_type(liste):
"""
Calcule l'écart-type d'une liste de nombres.
Écart-type = racine carrée de la variance.
Exemple:
>>> ecart_type([2, 4, 4, 4, 5, 5, 7, 9])
2.0
"""
# À compléter
pass
def minimum(liste):
"""
Retourne le minimum d'une liste (sans utiliser min()).
Exemple:
>>> minimum([5, 2, 8, 1, 9])
1
"""
# À compléter
pass
def maximum(liste):
"""
Retourne le maximum d'une liste (sans utiliser max()).
Exemple:
>>> maximum([5, 2, 8, 1, 9])
9
"""
# À compléter
pass
if __name__ == "__main__":
doctest.testmod(verbose=True)
```
---
## Partie 4 : Module conversion
### Exercice 3 : Créer le module `conversion.py`
```python
# mathtools/conversion.py
"""Module de conversions d'unités."""
import doctest
# --- Températures ---
def celsius_vers_fahrenheit(celsius):
"""
Convertit des degrés Celsius en Fahrenheit.
Formule : F = C × 9/5 + 32
Exemple:
>>> celsius_vers_fahrenheit(0)
32.0
>>> celsius_vers_fahrenheit(100)
212.0
>>> celsius_vers_fahrenheit(-40)
-40.0
"""
# À compléter
pass
def fahrenheit_vers_celsius(fahrenheit):
"""
Convertit des degrés Fahrenheit en Celsius.
Formule : C = (F - 32) × 5/9
Exemple:
>>> fahrenheit_vers_celsius(32)
0.0
>>> fahrenheit_vers_celsius(212)
100.0
"""
# À compléter
pass
# --- Distances ---
def km_vers_miles(km):
"""
Convertit des kilomètres en miles.
1 mile = 1.60934 km
Exemple:
>>> round(km_vers_miles(10), 2)
6.21
"""
# À compléter
pass
def miles_vers_km(miles):
"""
Convertit des miles en kilomètres.
Exemple:
>>> round(miles_vers_km(10), 2)
16.09
"""
# À compléter
pass
# --- Poids ---
def kg_vers_livres(kg):
"""
Convertit des kilogrammes en livres.
1 livre = 0.453592 kg
Exemple:
>>> round(kg_vers_livres(1), 2)
2.2
"""
# À compléter
pass
# --- Informatique ---
def octets_vers_kilo(octets):
"""
Convertit des octets en kilooctets (1 Ko = 1024 octets).
Exemple:
>>> octets_vers_kilo(2048)
2.0
"""
# À compléter
pass
def bits_vers_octets(bits):
"""
Convertit des bits en octets (1 octet = 8 bits).
Exemple:
>>> bits_vers_octets(64)
8.0
"""
# À compléter
pass
if __name__ == "__main__":
doctest.testmod(verbose=True)
```
---
## Partie 5 : Programme principal
### Exercice 4 : Créer `main.py`
Ce fichier sera le point d'entrée de votre package. Il propose un menu interactif.
```python
# mathtools/main.py
"""Programme principal de MathTools."""
from geometrie import *
from statistiques import *
from conversion import *
def menu():
"""Affiche le menu principal."""
print("\n" + "=" * 40)
print(" MATHTOOLS v1.0.0")
print("=" * 40)
print("1. Géométrie")
print("2. Statistiques")
print("3. Conversions")
print("0. Quitter")
print("=" * 40)
def menu_geometrie():
"""Sous-menu géométrie."""
print("\n--- GÉOMÉTRIE ---")
print("1. Aire d'un rectangle")
print("2. Aire d'un cercle")
print("3. Hypoténuse")
print("0. Retour")
choix = input("Votre choix : ")
if choix == "1":
l = float(input("Longueur : "))
L = float(input("Largeur : "))
print(f"Aire = {aire_rectangle(l, L)}")
elif choix == "2":
r = float(input("Rayon : "))
print(f"Aire = {round(aire_cercle(r), 4)}")
elif choix == "3":
a = float(input("Côté a : "))
b = float(input("Côté b : "))
print(f"Hypoténuse = {hypotenuse(a, b)}")
def menu_statistiques():
"""Sous-menu statistiques."""
print("\n--- STATISTIQUES ---")
print("Entrez vos valeurs séparées par des espaces :")
valeurs = input("> ")
liste = [float(x) for x in valeurs.split()]
print(f"Moyenne : {moyenne(liste)}")
print(f"Médiane : {mediane(liste)}")
print(f"Min : {minimum(liste)}")
print(f"Max : {maximum(liste)}")
def menu_conversions():
"""Sous-menu conversions."""
print("\n--- CONVERSIONS ---")
print("1. Celsius → Fahrenheit")
print("2. Fahrenheit → Celsius")
print("3. Km → Miles")
print("0. Retour")
choix = input("Votre choix : ")
if choix == "1":
c = float(input("Température en °C : "))
print(f"{c}°C = {celsius_vers_fahrenheit(c)}°F")
elif choix == "2":
f = float(input("Température en °F : "))
print(f"{f}°F = {fahrenheit_vers_celsius(f)}°C")
elif choix == "3":
km = float(input("Distance en km : "))
print(f"{km} km = {round(km_vers_miles(km), 2)} miles")
def main():
"""Boucle principale."""
while True:
menu()
choix = input("Votre choix : ")
if choix == "1":
menu_geometrie()
elif choix == "2":
menu_statistiques()
elif choix == "3":
menu_conversions()
elif choix == "0":
print("Au revoir !")
break
else:
print("Choix invalide.")
if __name__ == "__main__":
main()
```
---
## Partie 6 : Tests et validation
### Exercice 5 : Tester tous les modules
Exécutez chaque module pour vérifier que tous les doctests passent :
```bash
cd mathtools
python geometrie.py
python statistiques.py
python conversion.py
```
Tous les tests doivent afficher `ok`.
### Exercice 6 : Utiliser le package
Dans un nouveau fichier `test_mathtools.py` situé **en dehors** du dossier mathtools :
```python
# test_mathtools.py
from mathtools.geometrie import aire_cercle, hypotenuse
from mathtools.statistiques import moyenne, ecart_type
from mathtools.conversion import celsius_vers_fahrenheit
# Tests
print("Aire cercle r=5 :", round(aire_cercle(5), 2))
print("Hypoténuse 3,4 :", hypotenuse(3, 4))
print("Moyenne [10,20,30] :", moyenne([10, 20, 30]))
print("20°C en Fahrenheit :", celsius_vers_fahrenheit(20))
```
---
## Bonus : Documentation avancée
### Générer une documentation HTML
Installez `pydoc` (inclus avec Python) et générez la doc :
```bash
python -m pydoc -w mathtools.geometrie
python -m pydoc -w mathtools.statistiques
```
Cela génère des fichiers HTML consultables dans un navigateur.
### Ajouter des assertions
Améliorez vos fonctions avec des vérifications :
```python
def aire_cercle(rayon):
assert rayon > 0, "Le rayon doit être strictement positif"
return math.pi * rayon ** 2
```
---
## Résumé des notions
| Concept | Application dans ce TP |
|---------|------------------------|
| Module | Fichier .py contenant des fonctions |
| Package | Dossier avec `__init__.py` |
| Import | `from module import fonction` |
| Docstring | Documentation des fonctions |
| Doctest | Tests dans la documentation |
| `__name__` | Permet d'exécuter un module seul |
---
## Pour aller plus loin
- **pytest** : Framework de tests plus puissant que doctest
- **Sphinx** : Générateur de documentation professionnelle
- **PyPI** : Publier son package pour le monde entier
- **pip** : Installer des packages depuis PyPI
---
Auteurs : Florian Mathieu, Enzo Frémeaux, Thimothée Decooster
Licence CC BY NC
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.