Aller au contenu

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.

# 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 :

# 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

# 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

# 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.

# 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 :

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 :

# 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 :

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 :

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.