Introduction to Typing Extensions
With the advent of static typing in Python, the typing
module has become a cornerstone for developers aiming to write better-typed and more robust programs. However, the standard typing
module does not always evolve as quickly as the needs of Python developers, which is where typing-extensions
comes into play. The typing-extensions
library provides “forward compatibility” for type hints, bringing new typing features from future versions of Python to older versions. In this blog, we’ll take a comprehensive look at typing-extensions
, showcase its most useful APIs, and demonstrate its implementation through practical examples.
Key Features and APIs in Typing Extensions
Here are some of the APIs provided by typing-extensions
, along with examples to highlight their utility:
1. TypedDict
TypedDict
allows you to define a dictionary with strict typing for keys and values.
from typing_extensions import TypedDict class Movie(TypedDict): title: str rating: float movie: Movie = {"title": "Inception", "rating": 8.8} # movie["rating"] = "Excellent" # TypeError: rating must be a float
2. Literal
Literal
lets you specify that a value must be one of a predefined set of values.
from typing_extensions import Literal def order_pizza(size: Literal["small", "medium", "large"]) -> str: return f"Ordering a {size} pizza" print(order_pizza("medium")) # print(order_pizza("extra-large")) # TypeError: Invalid size
3. Protocol
Protocol
is used to define structural subtyping (duck typing).
from typing_extensions import Protocol class SupportsAddition(Protocol): def __add__(self, other) -> int: ... def add(a: SupportsAddition, b: SupportsAddition) -> int: return a + b print(add(5, 10)) # Outputs: 15
4. NotRequired
and Required
in TypedDict
These features enhance TypedDict
by allowing flexibility in defining optional and required keys.
from typing_extensions import TypedDict, NotRequired, Required class User(TypedDict): name: Required[str] email: NotRequired[str] user: User = {"name": "Alice"} print(user) # Outputs: {'name': 'Alice'}
5. Self
Self
is used to annotate methods that return an instance of their class.
from typing_extensions import Self class Builder: def set_name(self, name: str) -> Self: self.name = name return self def build(self) -> dict: return {"name": self.name} builder = Builder().set_name("My Builder").build() print(builder) # Outputs: {'name': 'My Builder'}
6. Annotated
Annotated
adds metadata to type hints.
from typing_extensions import Annotated def greet(name: Annotated[str, "Name of the person"]) -> str: return f"Hello, {name}" print(greet("World"))
7. Overload
Overload
allows function signatures to vary based on input types.
from typing_extensions import overload @overload def process(item: int) -> str: ... @overload def process(item: str) -> int: ... def process(item): if isinstance(item, int): return str(item) if isinstance(item, str): return len(item) print(process(42)) # Outputs: "42" print(process("hello")) # Outputs: 5
Typing Extensions in a Real-World App
Let’s build a basic application that demonstrates the above APIs in action.
Example: Online Bookstore
from typing_extensions import TypedDict, Literal, Protocol, overload, Annotated # Using TypedDict class Book(TypedDict): title: str price: float books: list[Book] = [ {"title": "Python Essentials", "price": 29.99}, {"title": "Effective Python", "price": 24.99}, ] # Using Literal def get_genre(genre: Literal["Fiction", "Non-Fiction", "Self-Help"]) -> str: return f"Genre: {genre}" print(get_genre("Fiction")) # Using Protocol class DiscountCalculator(Protocol): def calculate(self, price: float) -> float: ... class SimpleDiscount: def calculate(self, price: float) -> float: return price * 0.9 calculator = SimpleDiscount() print(calculator.calculate(100)) # Outputs: 90.0 # Annotating methods with metadata def print_invoice(amount: Annotated[float, "Total amount in USD"]) -> str: return f"Invoice Total: ${amount}" print(print_invoice(54.78))
This example demonstrates the power of typing-extensions
in structuring Python code effectively and safely, making it more readable and bug-free.
Conclusion
The typing-extensions
library is a must-have for developers who want to bring cutting-edge type hinting features to their Python projects, regardless of the Python version they are using. From TypedDict
to Literal
, and from Protocol
to Self
, typing-extensions
extends Python’s typing capabilities, making it invaluable for writing highly maintainable and robust Python applications. Try it in your projects today and unlock a new level of confidence in your codebase!