In Python, from __future__ import annotations
is a special import that changes how type annotations are handled by the interpreter.
Normally, when you annotate a function or class in Python, the interpreter evaluates the annotations immediately.
That means you can only reference types that are already defined.
But when you use:
from __future__ import annotations
👉 All annotations are stored as strings (instead of being evaluated immediately).
This is called "Postponed Evaluation of Annotations", introduced in PEP 563.
Forward References (Self-referencing classes)
Without it, you'd need to wrap type names in quotes if they refer to something not yet defined.
# Without future import
class Node:
def __init__(self, next: 'Node' = None): # must quote 'Node'
self.next = next
With from __future__ import annotations
:
from __future__ import annotations
class Node:
def __init__(self, next: Node | None = None): # no quotes needed
self.next = next
Avoid circular import issues
Since annotations are stored as strings, Python doesn’t need to resolve imports immediately.
This prevents many “ImportError: cannot import name …” problems in large projects.
Performance optimization
Storing annotations as strings delays evaluation until needed (e.g., by type checkers or typing.get_type_hints()
), which makes function/class definitions faster.
from __future__ import annotations
def square(x: int) -> int:
return x * x
class Tree:
def __init__(self, left: Tree | None = None, right: Tree | None = None):
self.left = left
self.right = right
Without the import, you’d get NameError: name 'Tree' is not defined
.
With the import, Python stores "Tree | None"
as a string, and everything works.
In Python 3.11+, this behavior (annotations
as strings) is default (PEP 563 was adopted).
So, you usually don’t need to write from __future__ import annotations
anymore unless you're working with Python <3.11.
👉 In short:
from __future__ import annotations
=
💡 “Don’t evaluate type hints right now, just save them as strings for later.”