PEP 649/749 : les annotations paresseuses
Python 3.14 change fondamentalement la façon dont les annotations de type sont traitées. Avec le PEP 649 (complété par le PEP 749), les annotations ne sont plus évaluées au moment de la définition de la classe ou de la fonction : elles sont stockées sous forme de descripteurs paresseux et ne sont résolues que lorsqu'on accède à __annotations__.
Différence avec PEP 563 (from __future__)
Le PEP 563 (from __future__ import annotations) transformait les annotations en chaînes de caractères. Le PEP 649 les conserve comme des expressions évaluables à la demande, ce qui préserve la compatibilité avec les bibliothèques de validation à l'exécution comme Pydantic ou attrs.
# Python 3.14 : les annotations sont évaluées paresseusement
# Plus besoin de from __future__ import annotations
class Noeud:
"""Référence en avant : fonctionne nativement."""
valeur: int
enfant: Noeud | None = None # pas d'erreur NameError !
# L'annotation 'Noeud' n'est pas évaluée à la définition.
# Elle est résolue uniquement quand on y accède :
print(Noeud.__annotations__) # {'valeur': <class 'int'>, 'enfant': ...}
# Avec get_annotations() pour un accès sûr :
import annotationlib
ann = annotationlib.get_annotations(Noeud, format=annotationlib.Format.FORWARDREF)
print(ann) # résout les références en avant correctement
Impact sur la vérification de types
Les bibliothèques qui inspectent les annotations à l'exécution (Pydantic, dataclasses, FastAPI) bénéficient directement de ce changement : elles reçoivent des objets Python réels au lieu de chaînes à parser, tout en supportant les références en avant.
from dataclasses import dataclass
@dataclass
class Arbre:
valeur: int
gauche: Arbre | None = None
droite: Arbre | None = None
def profondeur(self) -> int:
g = self.gauche.profondeur() if self.gauche else 0
d = self.droite.profondeur() if self.droite else 0
return 1 + max(g, d)
arbre = Arbre(
1,
gauche=Arbre(2, gauche=Arbre(4)),
droite=Arbre(3),
)
print(arbre.profondeur()) # 3
# Les annotations sont des types réels, pas des chaînes :
import annotationlib
ann = annotationlib.get_annotations(Arbre)
print(ann['gauche']) # Arbre | None
