from __future__ import annotations – la phrase qui ouvre l’avenir des annotations de type en Python

Mots-clés : __future__, annotations, PEP 563, PEP 649, Python 3.7+


1. Pourquoi __future__ était-il nécessaire ?



Python a été conçu comme un langage à typage dynamique dès le début des années 1990. Cependant, avec l’essor des projets de grande envergure, le contrôle statique des types et la lisibilité du code sont devenus essentiels. Pour répondre à ces besoins, Python a introduit les annotations de type (type hints) et le module typing.

Mais les premières annotations étaient évaluées immédiatement à l’exécution, ce qui posait plusieurs problèmes.

Problème Exemple
Références circulaires class A: pass
class B(A): pass
Utiliser A directement dans une annotation provoque un NameError
Évaluation immédiate from typing import List
def f(x: List[int]) -> List[int]: ...
La classe List doit être importée au moment de la définition de la fonction
Évaluation différée par chaîne def f(x: "MyClass") -> "MyClass": ...
Pour différer l’évaluation, il faut activer __future__

Pour résoudre ces problèmes, PEP 563 (Python 3.7) a introduit from __future__ import annotations.


2. Que signifie from __future__ import annotations ?

Définition : Depuis Python 3.7, cette directive permet de retarder l’évaluation de toutes les annotations de type en les stockant sous forme de chaînes de caractères.

Fonctionnement clé

  • À la définition d’une fonction ou d’une méthode, les annotations sont conservées sous forme de chaînes.
  • L’évaluation réelle se produit uniquement lorsqu’elle est requise (ex. via typing.get_type_hints).
  • Même en présence de références circulaires, aucune erreur NameError n’est levée.

Exemple d’utilisation

# sans __future__
class Node:
    def __init__(self, value: int, next: 'Node' = None):
        self.value = value
        self.next = next

# avec __future__
from __future__ import annotations

class Node:
    def __init__(self, value: int, next: Node | None = None):
        self.value = value
        self.next = next

Attention : Avec __future__, toutes les annotations deviennent des chaînes. Si vous utilisez typing.get_type_hints, le module typing évaluera automatiquement ces chaînes.


3. Évolutions après PEP 563



PEP 649 – redéfinition de l’évaluation différée

  • Introduit dans Python 3.11.
  • Même avec __future__, l’évaluation différée ne se produit que lorsqu’elle est réellement nécessaire.
  • typing.get_type_hints utilise désormais un cache pour améliorer les performances.

Après Python 3.12

  • PEP 649 est complètement implémenté ; l’évaluation différée devient encore plus efficace.
  • from __future__ import annotations devient le comportement par défaut ; la déclaration explicite est souvent superflue.

4. Conseils pratiques

Situation Méthode recommandée Raison
Références circulaires from __future__ import annotations Évite NameError
Projets de grande taille Déclaration __future__ + typing.get_type_hints Améliore les performances de vérification de type
Python 3.11+ Omettre la déclaration Le comportement par défaut est déjà différé
Annotations sous forme de chaîne Utiliser typing.TYPE_CHECKING Évite les imports inutiles à l’exécution

Exemple avec typing.TYPE_CHECKING

from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from .module_a import ClassA

class MyClass:
    def method(self, obj: ClassA) -> None:
        ...

TYPE_CHECKING est True uniquement pour les analyseurs statiques, ce qui empêche l’importation à l’exécution.


5. Conclusion

  • from __future__ import annotations rend possible l’évaluation différée des annotations de type, évitant ainsi les références circulaires et les erreurs d’exécution.
  • Depuis Python 3.11, l’évaluation différée est le comportement par défaut, rendant la déclaration souvent inutile.
  • Pour les versions 3.7‑3.10 ou lorsque vous avez besoin d’une différenciation explicite, cette directive reste indispensable.

Astuce : Appliquez __future__ à l’ensemble de votre base de code pour garantir une cohérence. Couplé à un vérificateur de type statique (mypy, pyright, etc.), vous améliorez considérablement la qualité du code.


Ressources supplémentaires - PEP 563 : https://www.python.org/dev/peps/pep-0563/ - PEP 649 : https://www.python.org/dev/peps/pep-0649/ - Documentation officielle de mypy : https://mypy.readthedocs.io/en/stable/cheat_sheet.html


Articles connexes :