Des annotations qui décrivent, sans imposer
Une annotation de type est une note que tu attaches à un nom — généralement un paramètre de fonction — qui dit « ça devrait être un int », « ceci renvoie une liste de chaînes », et ainsi de suite. Python ne les vérifie pas à l'exécution. Passer une chaîne là où tu as annoté un int ne lève pas d'erreur. Ton éditeur et des outils externes (mypy, pyright, Pylance de VS Code) lisent les annotations et te préviennent avant que le code ne s'exécute.
Le cas le plus simple possible :
name: str annote le paramètre. -> str annote la valeur de retour. Les deux appels s'exécutent. Le second est faux — un type checker statique le signalerait — mais Python lui-même le traite volontiers parce que 42 se trouve supporter l'interpolation f"{...}".
C'est le modèle mental crucial : les annotations sont de la documentation qu'une machine peut lire. Elles ne changent pas l'exécution.
Pourquoi se donner la peine ?
Trois gains concrets, dans l'ordre de la rapidité avec laquelle ils paient :
- Ton éditeur devient plus intelligent. L'auto-complétion montre les bonnes méthodes, les renommages se propagent correctement, et survoler une variable te dit son type.
- Les signatures de fonctions deviennent auto-descriptives.
def fetch(url: str, timeout: float = 5.0) -> dict:dit à un lecteur exactement quoi passer et ce qu'il va obtenir — pas besoin de lire le corps. - Les type checkers attrapent les erreurs avant que tu exécutes le code. Lancer
mypy .sur un projet fait remonter le genre de bugs que les tests unitaires ratent souvent — unNonerenvoyé là où tu attendais une valeur, un dict utilisé là où une liste va.
Pour un script d'un seul fichier qui n'est que pour toi et seulement pour aujourd'hui, saute les annotations. Pour tout ce sur quoi tu reviendras ou que tu partageras, les quinze secondes qu'elles prennent à écrire paient dans l'heure.
Types intégrés de base
Tu n'as pas besoin d'import pour ceux-ci :
Les annotations de variables (name: str = "Rosa") sont rarement nécessaires — Python déduit le type du côté droit. Garde-les pour les paramètres, les types de retour et le cas occasionnel où le type déduit est ambigu.
Les fonctions qui ne renvoient rien utilisent -> None :
Listes, dicts, tuples et sets
Les conteneurs ont besoin d'une seconde information — ce qu'ils contiennent. Python moderne te laisse indexer les types intégrés directement :
En les lisant à voix haute :
list[float]— une liste de floats.dict[str, int]— un dict avec clés string et valeurs int.tuple[float, float]— un tuple d'exactement deux floats.set[str]— un set de strings.
La syntaxe d'indexation list[...], dict[...] fonctionne en Python 3.9 et suivants. Dans du code plus ancien, tu verras List, Dict, Tuple importés depuis typing — même sens, orthographe plus ancienne.
Valeurs optionnelles
« Pourrait être None » est courant. Ça a deux orthographes équivalentes — les deux vont bien, mais la plus récente se lit mieux :
str | None veut dire « une chaîne, ou None ». La syntaxe | fonctionne en Python 3.10+. Dans du code plus ancien tu verras Optional[str] du module typing, qui veut dire la même chose.
Un appelant qui voit -> str | None sait qu'il faut vérifier None avant d'utiliser le résultat — c'est tout l'intérêt de l'annotation.
Types union : ceci ou cela
Quand une valeur pourrait être de plusieurs types, utilise | :
Tu peux unir plus de deux types. int | str | float veut dire « n'importe lequel de ces trois ».
Annoter des variables dans des fonctions
La plupart du temps, Python peut deviner le type d'une variable locale depuis son initialiseur. Tu as seulement besoin d'une annotation quand :
- Le conteneur commence vide et le type checker ne peut pas deviner son contenu.
- La valeur pourrait être de plusieurs types et tu veux t'engager sur un seul.
- Tu veux documenter l'intention pour un lecteur humain.
typing.Any est l'échappatoire — « je ne veux pas annoter ça précisément ». Utilise-le avec parcimonie. Abuser de Any rend le reste de tes annotations sans valeur.
Annoter des classes
Les attributs de classe et les signatures de méthode s'annotent de la même façon que n'importe quelle autre fonction :
Les dataclasses exigent en fait des annotations de type — le décorateur @dataclass les lit pour générer __init__ et __repr__. C'est le seul endroit où les annotations affectent le comportement à l'exécution.
Tuples et le cas « n'importe quelle longueur »
tuple[...] a deux formes qui embrouillent les débutants :
tuple[float, float]— exactement deux floats.tuple[int, ...]— un nombre quelconque d'ints. Le...(un vrai élément de syntaxe dans le système de types) veut dire « et ainsi de suite ».
Callables et alias de type
Quand une fonction prend ou renvoie une autre fonction, utilise Callable :
Callable[[int], int] veut dire « une fonction qui prend un int et renvoie un int ».
Quand une annotation devient répétitive, nomme-la :
Un alias est juste une affectation Python normale. Partout où tu utiliserais la forme longue, le nom court fonctionne.
Lancer un type checker
L'interpréteur de Python ignore les annotations de type. Pour vraiment les vérifier, installe un type checker. mypy est l'original ; pyright (utilisé par Pylance de VS Code) est plus rapide.
pip install mypy
mypy your_project/
Le premier passage fera remonter des erreurs à des endroits que tu n'avais pas réalisés. Travaille-les progressivement — # type: ignore fait taire une seule ligne quand tu dois avancer.
Les IDE modernes lancent le type checking en continu pendant que tu édites, donc la plupart du feedback arrive avant l'enregistrement.
Quand les annotations de type ne conviennent pas
- Scripts d'exploration rapide. Les annotations ajoutent de la friction à du code qui vit une heure.
- Code fortement dynamique. La métaprogrammation, les systèmes de plugins et motifs similaires dépassent souvent ce que le système de types peut décrire. Annote l'API externe et laisse l'intérieur libre.
- Bibliothèques tierces sans types. Si une bibliothèque que tu importes n'a pas d'infos de type,
Anyfuit dans ton code. Bon — ce n'est pas à toi de l'annoter.
Pour tout ce qui est entre les deux, les annotations de type sont une petite habitude avec un gros retour. Le coût, c'est quelques frappes de plus par signature de fonction. Le retour, c'est moins de bugs, des refactos plus faciles et du code qui se documente lui-même.
Ensuite : modules et imports
Tu as maintenant tout l'outillage au niveau fonction — arguments, décorateurs, annotations de type. Ensuite, on regarde comment Python organise le code à travers les fichiers : modules, packages et système d'import.
Questions fréquentes
Que sont les annotations de type en Python ?
Les annotations de type sont des notes qui décrivent les types attendus de variables, paramètres de fonction et valeurs de retour. Python lui-même ne les applique pas à l'exécution — elles servent aux outils (IDE, linters, type checkers comme mypy ou pyright) et aux humains qui lisent le code.
Les annotations de type rendent-elles Python plus rapide ?
Non. L'interpréteur Python ignore les annotations de type à l'exécution. L'accélération est dans ta boucle de développement — moins de fautes de frappe repérées par les éditeurs, signatures de fonctions plus claires, refactorisations plus sûres.
Quand devrais-je ajouter des annotations de type ?
Ajoute-les aux signatures de fonctions publiques — paramètres et types de retour. Ajoute-les avec parcimonie dans les corps de fonction, seulement là où le type d'une variable n'est pas évident. Pour des scripts à usage unique, elles sont optionnelles. Pour du code partagé et des bibliothèques, elles se paient rapidement.