from __future__ import annotations – de sleutel tot de toekomst van type hints in Python

Keywords: __future__, annotations, PEP 563, PEP 649, Python 3.7+


1. Waarom was __future__ nodig?



Python is sinds het begin van de jaren 90 een dynamisch getypeerde taal. Naarmate projecten groter werden, werd statische typecontrole en leesbaarheid steeds belangrijker. Om dit te ondersteunen introduceerde Python type hints via het typing-module.

Echter, de eerste type hints werden direct op runtime geïnterpreteerd, wat problemen veroorzaakte:

Probleem Voorbeeld
Cirkulair refereren class A: pass
class B(A): pass
Gebruik van A in een type hint leidt tot NameError
Vroege evaluatie from typing import List
def f(x: List[int]) -> List[int]: ...
De List moet al geïmporteerd zijn bij het definiëren van de functie
String lazy def f(x: "MyClass") -> "MyClass": ...
Om een type als string te laten uitstellen, is __future__ vereist

Om deze problemen op te lossen, introduceerde PEP 563 (Python 3.7) from __future__ import annotations.


2. Wat is from __future__ import annotations?

Definitie: Een functie geïntroduceerd in Python 3.7 die alle type hints als strings opslaat, waardoor ze later kunnen worden geëvalueerd.

Kernwerking

  • Bij het definiëren van functies/methodes worden type hints als strings opgeslagen.
  • De daadwerkelijke evaluatie vindt plaats wanneer een type nodig is (bijv. via typing.get_type_hints).
  • Cirkulair refereren werkt zonder NameError.

Voorbeeld

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

# met __future__
from __future__ import annotations

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

Let op: Met __future__ worden alle type hints strings, dus functies zoals typing.get_type_hints evalueren automatisch de strings.


3. Veranderingen na PEP 563



PEP 649 – herdefinitie van lazy evaluatie

  • In Python 3.11 werd PEP 649 geïntroduceerd.
  • Zelfs met __future__ gebeurt lazy evaluatie pas wanneer het echt nodig is.
  • typing.get_type_hints gebruikt een cache, wat de prestaties verbetert.

Na Python 3.12

  • PEP 649 is volledig geïmplementeerd, waardoor lazy evaluatie nog efficiënter is.
  • from __future__ import annotations is de standaard, dus meestal is een expliciete declaratie niet meer nodig.

4. Praktische tips

Situatie Aanbevolen aanpak Reden
Cirkulair refereren from __future__ import annotations Voorkomt NameError
Grote projecten __future__ + typing.get_type_hints Verbeterde prestaties bij typecontrole
Python 3.11+ Declaratie overbodig Lazy evaluatie is standaard
String type hints Gebruik typing.TYPE_CHECKING Voorkomt onnodige imports op runtime

Voorbeeld: typing.TYPE_CHECKING gebruiken

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 is True alleen voor statische typecheckers, dus er gebeurt geen import op runtime.

5. Afsluiting

  • from __future__ import annotations maakt lazy evaluatie van type hints mogelijk, waardoor cirkulaire referenties en runtime fouten worden voorkomen.
  • Vanaf Python 3.11 is lazy evaluatie de standaard, dus expliciete declaratie is meestal niet meer nodig.
  • Het blijft echter nuttig voor Python 3.7–3.10 of wanneer expliciete lazy evaluatie gewenst is.

Tip: Voeg __future__ toe aan je hele codebase voor consistentie. Samen met statische typecheckers (mypy, pyright, etc.) verhoog je de codekwaliteit aanzienlijk.


Extra bronnen - PEP 563: https://www.python.org/dev/peps/pep-0563/ - PEP 649: https://www.python.org/dev/peps/pep-0649/ - mypy officiële documentatie: https://mypy.readthedocs.io/en/stable/cheat_sheet.html


Bekijk ook: onderstaande artikelen!