Understanding future.annotations

Author

Andres Monge

Published

January 3, 2024

The __future__.annotations import is a special feature in Python that allows you to use new type hinting features in older versions of Python. This feature, introduced in PEP 563, postpones the evaluation of type annotations, turning them into strings that are evaluated at runtime instead of at function definition time.

How it Works

When you use from __future__ import annotations, Python sets a specific feature flag (CO_FUTURE_ANNOTATIONS) in the compiler. This flag instructs the compiler to treat type annotations as strings, which are then stored in the __annotations__ dictionary.

Example Usage

Here’s an example of how you might use this feature:

Code
from __future__ import annotations

def process_data(data: list[str]) -> None:
    # Process the data
    pass

# This will work without raising a TypeError in Python 3.7 and later.
process_data(["a", "b"])

To illustrate what happens without the __future__.annotations import, consider this example:

Code
def process_data(data: list[str]) -> None:
    # Process the data
    pass

# This will raise a TypeError in Python 3.9 and later.
process_data(["a", "b"])

Advantages

The postponed evaluation of type annotations has several advantages: - Resolves Forward References: Annotations can now refer to names that have not yet been defined, making it easier to handle recursive data structures. - Handles Cyclic References: It provides a simple way to deal with cyclic
references by importing names when running type checks and leaving them out at runtime. - Improves Runtime Performance: Type hints are no longer executed at module import
time, which is computationally expensive.

Compatibility

This feature is available starting with Python 3.7 and was planned to become the default behavior in Python 3.10. However, due to some inconsistencies, the default behavior has been postponed, and it remains an opt-in feature until further notice.