| name | python-typing |
| description | Guide complet des annotations de type Python (PEP 484, 526, 604, 585, etc.) — syntaxe, types génériques, protocoles, surcharges, et bonnes pratiques. En français. |
Annotations de Type Python — Guide Complet (Français)
Ce skill couvre l'ensemble du système de typage statique de Python moderne (3.10+). À utiliser avec Mypy/Pyright pour la vérification.
1. Types de Base
age: int = 25
nom: str = "Alice"
prix: float = 9.99
actif: bool = True
donnees: bytes = b"hello"
resultat: None = None
from typing import Any
valeur: Any = "n'importe quoi"
2. Types de Conteneurs
nombres: list[int] = [1, 2, 3]
chaines: list[str] = ["a", "b"]
mixtes: list[int | str] = [1, "deux"]
coordonnees: tuple[int, int] = (10, 20)
triplet: tuple[int, str, float] = (1, "a", 3.14)
suite: tuple[int, ...] = (1, 2, 3, 4, 5)
ages: dict[str, int] = {"Alice": 30, "Bob": 25}
config: dict[str, str | int | bool] = {"host": "localhost", "port": 8080}
tags: set[str] = {"python", "typing", "mypy"}
3. Types Union et Optionnel
identifiant: int | str = 42
identifiant = "abc123"
nom_optionnel: str | None = None
nom_optionnel = "Alice"
def trouver_utilisateur(id: int) -> dict[str, str] | None:
"""Recherche un utilisateur par son identifiant.
Args:
id: L'identifiant unique de l'utilisateur.
Returns:
Le profil utilisateur si trouvé, None sinon.
"""
...
4. Types Génériques Avancés
from typing import TypeVar, Generic, Sequence, Callable
T = TypeVar("T")
K = TypeVar("K")
V = TypeVar("V")
class Cache(Generic[K, V]):
"""Cache générique clé-valeur avec expiration."""
def __init__(self) -> None:
self._stockage: dict[K, V] = {}
def obtenir(self, cle: K) -> V | None:
return self._stockage.get(cle)
def definir(self, cle: K, valeur: V) -> None:
self._stockage[cle] = valeur
class FileAttente(Generic[T]):
"""File d'attente FIFO générique."""
def __init__(self) -> None:
self._elements: list[T] = []
def enfiler(self, element: T) -> None:
self._elements.append(element)
def defiler(self) -> T:
if not ._elements:
IndexError()
._elements.pop()
5. TypeVar avec Contraintes
from typing import TypeVar
Numerique = TypeVar("Numerique", int, float)
def multiplier(a: Numerique, b: Numerique) -> Numerique:
return a * b
multiplier(2, 3)
multiplier(2.5, 3.0)
Comparable = TypeVar("Comparable", bound="Comparable")
class ArbreBinaire(Generic[Comparable]):
def __init__(self, valeur: Comparable) -> None:
self.valeur = valeur
self.gauche: ArbreBinaire[Comparable] | None = None
self.droite: ArbreBinaire[Comparable] | None = None
6. Callable (Fonctions)
from typing import Callable
Transformateur: type = Callable[[int], str]
Filtre: type = Callable[[str], bool]
Comparateur: type = Callable[[int, int], int]
def appliquer(
valeurs: list[int],
operation: Callable[[int], str],
) -> list[str]:
"""Applique une transformation à chaque élément."""
return [operation(v) for v in valeurs]
Gestionnaire: type = Callable[..., None]
7. Literal
from typing import Literal
Direction = Literal["nord", "sud", "est", "ouest"]
NiveauLog = Literal["debug", "info", "warning", "error", "critical"]
MethodeHTTP = Literal["GET", "POST", "PUT", "DELETE", "PATCH"]
def se_deplacer(direction: Direction) -> None:
...
def journaliser(message: str, niveau: NiveauLog = "info") -> None:
...
CodeStatut: type = Literal[200, 201, 204] | Literal[400, 404, 500]
8. TypedDict
from typing import TypedDict, NotRequired
class ProfilUtilisateur(TypedDict):
"""Structure d'un profil utilisateur."""
id: int
nom: str
email: str
age: NotRequired[int]
roles: NotRequired[list[str]]
class ConfigApplication(TypedDict, total=False):
"""Tous les champs sont optionnels avec total=False."""
host: str
port: int
debug: bool
log_level: str
def creer_profil(donnees: ProfilUtilisateur) -> str:
return f"Profil créé : {donnees['nom']}"
profil: ProfilUtilisateur = {
"id": 1,
"nom": "Alice",
"email": "alice@exemple.com",
}
9. Protocol (Typage Structurel)
from typing import Protocol, runtime_checkable
class Ouvrable(Protocol):
"""Tout objet qui peut être ouvert."""
def ouvrir(self) -> str: ...
def fermer(self) -> None: ...
class Connexion:
def ouvrir(self) -> str:
return "Connecté"
def fermer(self) -> None:
print("Fermé")
class Fichier:
def ouvrir(self) -> str:
return "Fichier ouvert"
def fermer(self) -> None:
print("Fichier fermé")
def utiliser(ressource: Ouvrable) -> str:
resultat = ressource.ouvrir()
ressource.fermer()
return resultat
utiliser(Connexion())
utiliser(Fichier())
():
() -> : ...
([, , ], Iterable)
10. Annotations pour Fonctions Avancées
Surcharge (@overload) — PEP 484
from typing import overload
@overload
def rechercher(id: int) -> dict[str, str]: ...
@overload
def rechercher(nom: str) -> list[dict[str, str]]: ...
def rechercher(id_nom: int | str) -> dict[str, str] | list[dict[str, str]]:
"""Recherche un utilisateur par ID ou par nom.
Args:
id_nom: Identifiant numérique ou nom à rechercher.
Returns:
Un profil unique si recherche par ID, une liste si par nom.
"""
if isinstance(id_nom, int):
return {"id": str(id_nom), "nom": "Trouvé"}
return [{"id": "1", "nom": id_nom}]
ParamSpec (typage de décorateurs)
from typing import Callable, TypeVar
from typing import ParamSpec
P = ParamSpec("P")
R = TypeVar("R")
def journaliser(
fonction: Callable[P, R],
) -> Callable[P, R]:
"""Décorateur qui journalise les appels de fonction.
Conserve la signature originale grâce à ParamSpec.
"""
from functools import wraps
@wraps(fonction)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
print(f"Appel : {fonction.__name__}({args}, {kwargs})")
return fonction(*args, **kwargs)
return wrapper
@journaliser
def additionner(a: int, b: int) -> int:
return a + b
11. Final et ClassVar
from typing import Final, ClassVar
MAX_CONNEXIONS: Final[int] = 100
PI: Final[float] = 3.14159
class Compteur:
total: ClassVar[int] = 0
def __init__(self) -> None:
Compteur.total += 1
self.valeur: int = 0
12. TypeGuard et TypeIs (Python 3.10+ / 3.13+)
from typing import TypeGuard, TypeIs
def est_liste_entiers(valeur: object) -> TypeGuard[list[int]]:
"""Vérifie qu'une valeur est une liste d'entiers."""
return isinstance(valeur, list) and all(isinstance(x, int) for x in valeur)
def est_entier(valeur: object) -> TypeIs[int]:
"""Affine le type : si True, c'est un int. Si False, ce n'est PAS un int."""
return isinstance(valeur, int)
def traiter(valeur: int | str) -> str:
if est_entier(valeur):
return str(valeur * 2)
else:
return valeur.upper()
13. Types pratiques pour Cas Courants
from pathlib import Path
from datetime import datetime
from collections.abc import Sequence, Mapping, Iterable, Iterator
from typing import Any
chemin_config: Path = Path("/etc/config.json")
date_creation: datetime = datetime.now()
def traiter_sequence(elements: Sequence[int]) -> int:
"""Accepte list, tuple, range, etc."""
return sum(elements)
def lire_config(source: Mapping[str, Any]) -> str:
return source.get("nom", "inconnu")
def generer_ids() -> Iterator[int]:
i = 1
while True:
yield i
i += 1
14. Bonnes Pratiques
- Activer la vérification stricte :
mypy --strict ou pyright en mode strict
- Éviter
Any — c'est une échappatoire, pas une solution
- Préférer
X | None à Optional[X] (PEP 604)
- Préférer
list[X] à typing.List[X] (PEP 585)
- Utiliser
collections.abc.Sequence plutôt que list pour les paramètres (plus flexible)
- TypedDict > dict[str, Any] pour les structures connues
- Ne pas annoter
self dans les méthodes
- Ne pas annoter
cls dans @classmethod
- Les variables de classe se distinguent avec
ClassVar
Références