python Annotated
python Annotated
In Python, Annotated is a powerful feature from the typing module (introduced in Python 3.9) that allows you to attach arbitrary metadata to your type hints.
Standard type hints only tell you what data type to expect (e.g., x: int). Annotated lets you add context-specific details (e.g., x: Annotated[int, ValueRange(1, 10)]) without breaking standard type checkers like Mypy.
Core Syntax
The syntax requires at least two arguments:
-
The first argument must be a valid Python type.
-
All following arguments are the metadata you want to attach (strings, objects, functions, etc.).
Here is a basic example:
from typing import Annotated
# A string type hint with documentation metadata
Username = Annotated[str, "Must be alphanumeric", "Length 3-20"]
def register_user(name: Username):
pass
Why Use It? (3 Main Use Cases)
Standard type checkers completely ignore the metadata inside Annotated. However, runtime libraries, frameworks, and your own custom scripts can read this metadata to automate tasks.
1. Data Validation (FastAPI & Pydantic)
Modern web frameworks use Annotated heavily to clean up function signatures and enforce validation rules like minimum lengths or numeric ranges.
from fastapi import FastAPI, Query
from typing import Annotated
app = FastAPI()
@app.get("/items/")
async def read_items(
# Validates that 'q' is a string, optional, and max 50 chars long
q: Annotated[str | None, Query(max_length=50)] = None
):
return {"q": q}
2. Dependency Injection
Frameworks like FastAPI or SQLModel use it to inject database sessions or authentication states cleanly.
from fastapi import Depends, FastAPI
from typing import Annotated
def get_db():
db = DBConnection()
try:
yield db
finally:
db.close()
# Create a reusable, type-hinted dependency
DatabaseSession = Annotated[DBConnection, Depends(get_db)]
@app.get("/users")
def list_users(db: DatabaseSession):
# 'db' is automatically populated and correctly type-hinted
return db.query_users()
3. Static Analysis and Custom Code Generation
If you are writing a library (like a CLI parser or an ORM), you can inspect Annotated metadata at runtime to automatically generate fields or documentation.
How to Inspect Metadata at Runtime
To extract the metadata from an Annotated type, you cannot use standard tools like isinstance(). Instead, use typing.get_type_hints() with the include_extras=True flag.
from typing import Annotated, get_type_hints
# Define a type with custom metadata objects
class FieldValue:
def __init__(self, description: str):
self.description = description
Price = Annotated[float, FieldValue("Price in USD, must be positive")]
def process_payment(amount: Price):
pass
# Extracting the metadata
hints = get_type_hints(process_payment, include_extras=True)
price_hint = hints['amount']
print(price_hint) # Output: typing.Annotated[float, __main__.FieldValue]
print(price_hint.__meta__) # Output: (<__main__.FieldValue object at 0x...>,)
Version Note: If you are using Python 3.7 or 3.8,
Annotatedis not built into the standard library. You can still use it by installing the compatibility backport package viapip install typing_extensionsand importing it from there.
浙公网安备 33010602011771号