21  Les docstrings

Un code qui fonctionne est un bon début ; un code compréhensible par un collègue (ou par vous-même dans 6 mois) est bien mieux. Les docstrings sont l’outil Python standard pour documenter vos fonctions, modules et classes. Ce chapitre est court mais essentiel : le TOSA teste les docstrings au niveau Opérationnel et Avancé.

21.1 Les commentaires (#) — rappel

Les commentaires (#) sont ignorés par l’interpréteur. Ils expliquent un choix dans le code.

# Ceci est un commentaire, Python l'ignore
x = 10          # commentaire en fin de ligne

Règle : un bon commentaire explique le POURQUOI, pas le QUOI. Le code dit déjà ce qu’il fait ; le commentaire doit apporter un contexte non évident.

# ❌ Inutile : redit ce que fait le code
x = x + 1    # ajoute 1 à x

# ✅ Utile : explique une décision non évidente
x = x + 1    # on compense l'indice qui commence à 0 côté base de données

21.2 Les docstrings

Une docstring (documentation string) est une chaîne de documentation placée :

  • Juste après la signature d’une fonction, d’une classe ou d’un module.
  • Entre triples guillemets """...""".
def moyenne(notes):
    """Calcule la moyenne d'une liste de notes."""
    return sum(notes) / len(notes)

print(moyenne([12, 15, 14]))
13.666666666666666

Contrairement à un commentaire #, la docstring :

  1. Est associée à l’objet (fonction, classe…).
  2. Est accessible à l’exécution via __doc__ ou help().
  3. Est affichée par les outils de documentation (Sphinx, IDE, etc.).

Accéder à une docstring

Deux façons :

def moyenne(notes):
    """Calcule la moyenne d'une liste de notes."""
    return sum(notes) / len(notes)

# Méthode 1 : attribut __doc__
print(moyenne.__doc__)
Calcule la moyenne d'une liste de notes.

La seconde méthode est help() (à utiliser dans l’interpréteur interactif) :

help(moyenne)
# Help on function moyenne in module __main__:
#
# moyenne(notes)
#     Calcule la moyenne d'une liste de notes.

help() est particulièrement pratique dans l’interpréteur interactif : tapez help(print) pour voir l’aide d’une fonction native.

Docstring courte (une ligne)

Pour des fonctions simples :

def carre(x):
    """Renvoie le carré de x."""
    return x * x

print(carre.__doc__)
Renvoie le carré de x.

Convention : commencer par un verbe d’action (Renvoie, Calcule, Affiche…) et terminer par un point.

Docstring longue (multi-lignes)

Pour des fonctions plus complexes, on structure la docstring :

def statistiques(notes):
    """Calcule min, max et moyenne d'une liste de notes.

    Arguments:
        notes: liste de nombres (entiers ou flottants).

    Renvoie:
        Un tuple (minimum, maximum, moyenne).

    Exemple:
        >>> statistiques([12, 15, 9])
        (9, 15, 12.0)
    """
    return min(notes), max(notes), sum(notes) / len(notes)

print(statistiques.__doc__)
Calcule min, max et moyenne d'une liste de notes.

    Arguments:
        notes: liste de nombres (entiers ou flottants).

    Renvoie:
        Un tuple (minimum, maximum, moyenne).

    Exemple:
        >>> statistiques([12, 15, 9])
        (9, 15, 12.0)
    

La structure :

  1. Première ligne : résumé en une phrase.
  2. Ligne vide.
  3. Paragraphe(s) détaillés : arguments, retour, exemples, exceptions.
Conventions de style

Il existe plusieurs styles de docstrings :

  • Google (lisible, populaire) :

    Args:
        nom: description
    Returns:
        description
  • NumPy (utilisé en data science) :

    Parameters
    ----------
    nom : type
        description
  • reStructuredText / Sphinx (plus formel, plus ancien).

Choisissez-en un et tenez-vous y dans un projet. Le TOSA ne teste pas un style précis, mais la présence et la qualité des docstrings.

21.3 Les annotations de type (type hints)

Python 3 permet d’annoter les paramètres et le retour d’une fonction avec leur type attendu. C’est optionnel mais très utile.

def moyenne(notes: list) -> float:
    """Calcule la moyenne d'une liste de notes."""
    return sum(notes) / len(notes)

print(moyenne([12, 15, 14]))
13.666666666666666

Syntaxe :

  • param: type → type attendu pour le paramètre.
  • -> type après la signature → type du retour.

Exemples d’annotations

def saluer(nom: str, age: int) -> str:
    """Renvoie un message de salutation."""
    return f"Bonjour {nom}, vous avez {age} ans."

print(saluer("Alice", 30))
Bonjour Alice, vous avez 30 ans.
def additionner(a: float, b: float) -> float:
    return a + b

print(additionner(3.5, 2.7))
6.2

Les types hints sont des indications, pas des contraintes

⚠️ Python ne vérifie PAS les types à l’exécution

Les annotations sont informatives. Python ne plante pas si on passe un mauvais type :

def additionner(a: int, b: int) -> int:
    return a + b

print(additionner("abc", "def"))    # pas d'erreur ! concaténation de str
abcdef

Pour une vraie vérification, il faut un outil externe comme mypy (vu en Partie 4).

Les annotations servent surtout :

  • De documentation (plus précise qu’un commentaire).
  • D’aide à l’IDE (autocomplétion, détection d’erreurs à l’écriture).
  • Pour les outils de vérification statique (mypy).

Python 3.10 — syntaxe union avec |

Depuis Python 3.10, on peut écrire int | str pour « int OU str » :

def formater(valeur: int | float | str) -> str:
    """Convertit toute valeur numérique ou textuelle en chaîne."""
    return f"Valeur : {valeur}"

print(formater(42))
print(formater(3.14))
print(formater("bonjour"))
Valeur : 42
Valeur : 3.14
Valeur : bonjour

Avant Python 3.10, il fallait écrire Union[int, float, str] en important typing.Union.

21.4 Bonnes pratiques de nommage (PEP 8 résumé)

La PEP 8 est la norme de style Python. Résumé :

Élément Style Exemple
Variable, fonction snake_case calculer_moyenne
Classe PascalCase Utilisateur
Constante SCREAMING_SNAKE TAUX_TVA = 0.20
Module snake_case mon_module.py
Privé (convention) _underscore _interne
# ✅ Conforme PEP 8
TAUX_TVA = 0.20

def calculer_prix_ttc(prix_ht):
    return prix_ht * (1 + TAUX_TVA)

class Produit:
    def __init__(self, nom, prix):
        self.nom = nom
        self.prix = prix
        self._reference_interne = None   # privé (convention)

21.5 Le principe DRY (Don’t Repeat Yourself)

« Don’t Repeat Yourself » (ne vous répétez pas) est un principe fondamental :

Chaque morceau de logique doit exister en un seul endroit dans votre code.

# ❌ Violation DRY : on répète "taux_tva = 0.20"
def prix_ttc_pain(prix_ht):
    return prix_ht * 1.20

def prix_ttc_lait(prix_ht):
    return prix_ht * 1.20

def prix_ttc_fromage(prix_ht):
    return prix_ht * 1.20
# ✅ Respect DRY : une fonction, une constante
TAUX_TVA = 0.20

def prix_ttc(prix_ht, taux=TAUX_TVA):
    return prix_ht * (1 + taux)

print(prix_ttc(10))
print(prix_ttc(5.50))
12.0
6.6

Si la TVA change, on modifie une seule ligne. C’est aussi l’argument principal pour créer une fonction dès qu’on écrit deux fois le même code.


🧩 Quiz 2.1 — Documentation

Question 1

Comment accède-t-on à la docstring d’une fonction f ?

  1. f.doc
  2. f.__doc__
  3. docstring(f)
  4. help.doc(f)

b) f.__doc__ — attribut spécial de tout objet documenté. La fonction help(f) fait la même chose de façon plus lisible.

def f():
    """Exemple de docstring."""
    pass

print(f.__doc__)
Exemple de docstring.

Question 2

Quelle est la syntaxe correcte d’une docstring ?

  1. # Ceci est une docstring
  2. <docstring>Ceci est une docstring</docstring>
  3. """Ceci est une docstring."""
  4. /* Ceci est une docstring */

c) """Ceci est une docstring.""" — triple guillemets (ou triple apostrophes '''...'''), placée juste après la signature.

Question 3

Que fait Python avec les annotations de type ?

  1. Il vérifie les types à l’exécution et lève une erreur si faux
  2. Il les ignore à l’exécution (purement informatives)
  3. Il convertit automatiquement les arguments au bon type
  4. Il refuse d’exécuter le code si un type ne correspond pas

b) Il les ignore — les annotations sont purement informatives pour Python lui-même. Seuls des outils externes comme mypy les exploitent pour vérifier.

def f(x: int) -> int:
    return x * 2

print(f("abc"))    # pas d'erreur, Python ne vérifie pas
abcabc

Question 4

Quel nom respecte la PEP 8 pour une fonction ?

  1. CalculerMoyenne
  2. calculerMoyenne
  3. calculer_moyenne
  4. CALCULER_MOYENNE

c) calculer_moyenne — PEP 8 impose snake_case pour les fonctions et variables. Les autres styles correspondent à : PascalCase (classes), camelCase (autres langages), SCREAMING_SNAKE (constantes).

Question 5

Que signifie « DRY » ?

  1. Code sec et sans erreur
  2. Don’t Repeat Yourself — ne pas se répéter
  3. Dynamic Runtime Yield
  4. Do Reuse Yield

b) Don’t Repeat Yourself — principe qui recommande de ne pas dupliquer de logique. Dès qu’on écrit deux fois le même code, il y a un motif à extraire en fonction ou en constante.

Question 6

Où placer la docstring d’une fonction ?

  1. Avant le def, en commentaire #
  2. Juste après le def, indentée dans le bloc de la fonction
  3. À la fin de la fonction
  4. Dans un fichier séparé

b) Juste après le def — c’est la première instruction indentée du bloc. Python la reconnaît automatiquement comme __doc__.

def f(x):
    """Docstring placée correctement."""
    return x * 2

print(f.__doc__)
Docstring placée correctement.

✏️ Exercice 2.1 — Documenter une fonction existante

Reprenez la fonction suivante et ajoutez-y :

  1. Une docstring multi-lignes décrivant son but, ses arguments et son retour.
  2. Des annotations de type.
def prix_ttc(prix_ht, taux):
    return prix_ht * (1 + taux)
def prix_ttc(prix_ht, taux):
    return prix_ht * (1 + taux)

# Ajoutez la docstring et les annotations
def prix_ttc(prix_ht: float, taux: float = 0.20) -> float:
    """Calcule le prix TTC à partir d'un prix HT et d'un taux de TVA.

    Arguments:
        prix_ht: prix hors taxes en euros.
        taux: taux de TVA (par défaut 0.20 pour 20 %).

    Renvoie:
        Le prix TTC correspondant (float).

    Exemple:
        >>> prix_ttc(100)
        120.0
        >>> prix_ttc(100, taux=0.055)
        105.5
    """
    return prix_ht * (1 + taux)

# Vérification
print(prix_ttc(100))
print(prix_ttc.__doc__)
120.0
Calcule le prix TTC à partir d'un prix HT et d'un taux de TVA.

    Arguments:
        prix_ht: prix hors taxes en euros.
        taux: taux de TVA (par défaut 0.20 pour 20 %).

    Renvoie:
        Le prix TTC correspondant (float).

    Exemple:
        >>> prix_ttc(100)
        120.0
        >>> prix_ttc(100, taux=0.055)
        105.5
    

✏️ Exercice 2.2 — Module documenté

Écrivez une fonction resumer(texte, nb_mots=20) qui renvoie les nb_mots premiers mots d’un texte, suivis de "..." si le texte est plus long. Documentez-la avec une docstring complète et des annotations de type.

def resumer(texte, nb_mots=20):
    ...

# Tester
print(resumer("Python est un langage de programmation puissant et populaire dans le monde entier.", nb_mots=5))
def resumer(texte: str, nb_mots: int = 20) -> str:
    """Renvoie les premiers mots d'un texte, tronqué si nécessaire.

    Arguments:
        texte: chaîne de caractères à résumer.
        nb_mots: nombre maximum de mots à garder (défaut 20).

    Renvoie:
        Une chaîne contenant au maximum nb_mots mots, suivie de "..."
        si le texte original était plus long.

    Exemple:
        >>> resumer("Python est un langage puissant.", nb_mots=3)
        'Python est un...'
    """
    mots = texte.split()
    if len(mots) <= nb_mots:
        return texte
    return " ".join(mots[:nb_mots]) + "..."

# Tests
print(resumer("Python est un langage puissant.", nb_mots=3))
print(resumer("Bonjour", nb_mots=5))
print(resumer("Python est un langage de programmation puissant et populaire dans le monde entier.", nb_mots=5))
Python est un...
Bonjour
Python est un langage de...

À retenir

Points clés du chapitre
  1. Commentaire # : explique le pourquoi, ignoré par l’interpréteur.
  2. Docstring """...""" : documentation attachée à l’objet, accessible via __doc__ et help().
  3. Placée juste après def, class, ou en tête de module.
  4. Annotations de type : def f(x: int) -> int: — informatives, non vérifiées à l’exécution.
  5. Python 3.10+ : syntaxe int | str pour les unions.
  6. PEP 8 : snake_case (fonctions, variables), PascalCase (classes), SCREAMING_SNAKE (constantes).
  7. Principe DRY : ne jamais dupliquer de logique — extraire en fonction ou constante.

← Chapitre précédent : Les fonctionsChapitre suivant : Les f-strings →