Comprenez ce que les annotations font réellement à l'exécution et exploitez les dataclasses avec frozen, slots et default_factory.
Ouvrir cette leçon dans KodokonLes annotations de type sont des métadonnées, pas des contraintes : l'interpréteur les range dans __annotations__ et ne les vérifie jamais à l'exécution. La vérification est le travail d'un analyseur statique comme mypy ou pyright, avant l'exécution. Conséquence directe : un programme mal typé s'exécute sans broncher - et c'est voulu. Le typage graduel permet d'annoter progressivement un code existant sans rien casser.
from typing import get_type_hints
def add(a: int, b: int) -> int:
return a + b
print(add("py", "thon"))
print(add.__annotations__)
print(get_type_hints(add))Depuis Python 3.9, les types natifs sont directement génériques : écrivez list[int] ou dict[str, float] sans importer typing.List. Les unions s'écrivent int | None depuis 3.10. Pour lier le type d'entrée au type de sortie, utilisez un TypeVar - Python 3.12 offre même la syntaxe compacte def first[T](items: list[T]) -> T. Point souvent ignoré : from __future__ import annotations rend toutes les annotations paresseuses (stockées comme chaînes), ce qui autorise les références à des classes définies plus bas et allège le coût à l'import.
from collections.abc import Iterable, Iterator
from typing import TypeVar
T = TypeVar("T")
def dedupe(items: Iterable[T]) -> Iterator[T]:
seen: set[T] = set()
for item in items:
if item not in seen:
seen.add(item)
yield item
print(list(dedupe([3, 1, 3, 2, 1])))from dataclasses import dataclass, field
@dataclass(frozen=True, slots=True)
class Point:
x: float
y: float
@dataclass
class Basket:
items: list[str] = field(default_factory=list)
total: int = 0
def __post_init__(self) -> None:
self.total = len(self.items)
point = Point(1.0, 2.0)
print(point, hash(point))
print(Basket(["book", "pen"]))Trois paramètres font de @dataclass un outil de niveau production. frozen=True rend les instances immuables et, combiné à l'égalité générée, hashables - utilisables comme clés de dictionnaire. slots=True (Python 3.10+) supprime le __dict__ par instance : mémoire réduite, accès aux attributs plus rapides, et toute affectation d'attribut imprévu échoue. Enfin kw_only=True force les appels explicites par mot-clé. __post_init__ sert aux invariants et aux champs dérivés. Pour un simple agrégat immuable, NamedTuple reste pertinent ; dès qu'il y a comportement ou validation, préférez la dataclass.
add('py', 'thon') alors que la fonction est annotée (a: int, b: int) -> int ?items: list[str] = [] est-il interdit comme défaut d'un champ de dataclass ?list[str] n'est pas une annotation valide pour un champfrozen=True sur une dataclass ?