Python est dynamiquement typé : on ne déclare pas les types. C'est pratique pour prototyper, mais dangereux à grande échelle — un bug de type se découvre seulement à l'exécution. Depuis Python 3.5, on peut ajouter des annotations de types (type hints) qui, sans ralentir le code, permettent à des outils comme mypy de détecter les erreurs avant exécution. C'est une compétence très valorisée en entreprise.
Pourquoi annoter ?
Sans annotations :
def calculer_total(prix, quantite, tva):
return prix * quantite * (1 + tva)
# Que veut-on passer ? Des nombres ? Des listes ?
# Aucun moyen de savoir sans lire le corps de la fonction.Avec annotations :
def calculer_total(prix: float, quantite: int, tva: float) -> float:
"""Calcule le total TTC."""
return prix * quantite * (1 + tva)Maintenant :
- La signature est auto-documentée : on voit les types attendus et le retour.
- L'éditeur (VS Code, PyCharm) peut vérifier pendant que vous tapez.
mypypeut analyser tout le projet avant exécution.
Syntaxe de base
Variables
age: int = 30
nom: str = "Alice"
pi: float = 3.14
est_actif: bool = True
notes: list = [12, 15, 14]L'annotation vient après le nom, séparée par :.
Fonctions : paramètres et retour
def saluer(nom: str) -> str:
return f"Bonjour {nom}"
def age_est_majeur(age: int) -> bool:
return age >= 18
def afficher(message: str) -> None: # ne renvoie rien
print(message)-> None signifie que la fonction ne renvoie rien (ou None implicite).
Paramètres avec valeur par défaut
def saluer(nom: str, politesse: str = "Bonjour") -> str:
return f"{politesse} {nom}"Les types de base
Types primitifs
| Type Python | Annotation |
|---|---|
| Entier | int |
| Flottant | float |
| Chaîne | str |
| Booléen | bool |
| Octets | bytes |
| Rien | None |
Collections (avant Python 3.9)
Pour les collections typées, on devait importer depuis typing :
from typing import List, Dict, Tuple, Set
notes: List[int] = [12, 15, 14]
ages: Dict[str, int] = {"Alice": 30}
point: Tuple[float, float] = (3.0, 4.0)
valeurs: Set[str] = {"a", "b", "c"}Collections (Python 3.9+) — syntaxe moderne
Depuis Python 3.9, on peut utiliser directement les types builtin avec des crochets :
notes: list[int] = [12, 15, 14]
ages: dict[str, int] = {"Alice": 30, "Bob": 25}
point: tuple[float, float] = (3.0, 4.0)
valeurs: set[str] = {"a", "b", "c"}
# Plus besoin d'importer !Adoptez cette syntaxe — c'est la norme actuelle.
Types optionnels et unions
Optional : peut être None
Souvent, une valeur peut être None. On l'exprime avec Optional ou | None (Python 3.10+).
# Ancienne syntaxe
from typing import Optional
def chercher(nom: str) -> Optional[str]:
# peut renvoyer str OU None
if nom == "Alice":
return "trouvé"
return None
# Python 3.10+ : syntaxe avec |
def chercher_moderne(nom: str) -> str | None:
if nom == "Alice":
return "trouvé"
return NoneLes deux sont équivalents. Préférez str | None pour du code moderne.
Union : plusieurs types possibles
# Ancienne syntaxe
from typing import Union
def convertir(valeur: Union[int, str]) -> int:
return int(valeur)
# Python 3.10+
def convertir_moderne(valeur: int | str) -> int:
return int(valeur)
print(convertir_moderne(42))
print(convertir_moderne("100"))La syntaxe | est lisible et identique à celle qu'on utilise en pattern matching.
Any : échappatoire
Any désactive la vérification de type. À utiliser avec parcimonie — signifie « je ne sais pas / tout va ».
from typing import Any
def stocker_n_importe_quoi(x: Any) -> None:
"""Accepte n'importe quel type."""
print(f"Stocké : {x} (type {type(x).__name__})")
stocker_n_importe_quoi(42)
stocker_n_importe_quoi("Hello")
stocker_n_importe_quoi([1, 2, 3])Any trahit l'intérêt du typage — ne l'utilisez que pour du code qui accepte vraiment tout type (décorateurs génériques, APIs très flexibles).