Indications de type pratiques en Python
Les indications de type documentent les contrats attendus et facilitent analyse statique, édition et refactorisation. Python n'impose pas les annotations de fonctions ou variables à l'exécution. Validez séparément les entrées non fiables.
from collections.abc import Iterable
def average(values: Iterable[float]) -> float:
numbers = list(values)
if not numbers:
raise ValueError("values must not be empty")
return sum(numbers) / len(numbers)
Annotez d'abord les frontières publiques et les modèles internes importants. Les variables locales que le vérificateur peut inférer ont rarement besoin d'annotations.
Cibler une version de Python
La syntaxe de typage évolue. Déclarez la version minimale du projet, configurez le vérificateur en conséquence et utilisez typing_extensions lorsqu'une fonctionnalité récente doit être rétroportée.
À partir de Python 3.10, préférez les génériques intégrés et les unions | :
def lookup(names: list[str], fallback: str | None = None) -> str: ...
L'instruction type et la syntaxe de paramètres génériques entre crochets exigent Python 3.12 ou ultérieur. Utilisez les formes historiques TypeAlias et TypeVar si la cible l'exige.
Modéliser le sens, pas les détails de stockage
- Acceptez des capacités abstraites comme
Iterable,SequenceouMappingsi aucun conteneur concret n'est requis. - Renvoyez un type concret si l'appelant dépend de son comportement.
- Employez
Literalou une énumération pour un petit ensemble fermé de valeurs significatives. - Utilisez
TypedDictpour les enregistrements en forme de dictionnaire, et des classes de données ou ordinaires pour les valeurs avec comportement et invariants. - Employez
Protocolpour les interfaces structurelles ; l'héritage n'est pas requis. - Limitez
Anyaux frontières non typées et raffinez-le vite.Anydésactive les contrôles utiles au lieu de signifier « inconnu mais sûr ».
from typing import Protocol
class SupportsClose(Protocol):
def close(self) -> None: ...
def finish(resource: SupportsClose) -> None:
resource.close()
Raffinement et absence
Traitez les unions à l'aide de preuves réelles à l'exécution :
def length(value: str | bytes | None) -> int:
if value is None:
return 0
if isinstance(value, bytes):
return len(value)
return len(value)
Optional[T] signifie T | None ; il ne signifie pas que le paramètre a une valeur par défaut. N'utilisez pas cast à la place d'un contrôle : l'appel ne change que la vue du vérificateur statique.
Frontière d'exécution
Les annotations peuvent être représentées ou évaluées différemment selon les versions prises en charge. Le code qui les introspecte doit utiliser des API documentées comme typing.get_type_hints et tenir compte des imports, références anticipées et effets possibles de l'évaluation.
Un flux utile exécute un vérificateur configuré en CI, traite les suppressions comme des exceptions étroites et documentées, et teste séparément le comportement réel. L'accord du vérificateur ne prouve pas la correction ; il indique qu'une classe d'incohérences de contrats n'a pas été trouvée.
Sources
Explorer les liens
Cette note n’a pas encore de liens vers d’autres notes.