ajout de tous les cours et TP préparés cet été
This commit is contained in:
569
Modularité/TP_MiniPackage.md
Normal file
569
Modularité/TP_MiniPackage.md
Normal file
@@ -0,0 +1,569 @@
|
||||
# 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>.
|
||||
Reference in New Issue
Block a user