# 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 Licence Creative Commons
Ce cours est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International.