3 min read · 512 words
Python’s type hinting system has evolved from a quiet PEP into an indispensable tool for growing codebases. If you are still writing raw Python without type hints, you are missing out on instant IDE feedback, bulletproof refactoring, and self-documenting code.
Let's cut the theory. Here is a practical, no-fluff cheat sheet for the type hints you will actually use in production every day.
Keep it simple. Scalars use standard Python types (str, int, float, bool), while collections require imports from the typing module or modern built-in generics (supported in Python 3.9+).
from typing import Dict, List, Optional, Tuple, Union
name: str = "Claude"
age: int = 5
is_active: bool = True
price: float = 99.9
tags: List[str] = ["ai", "llm", "python"]
coordinates: Tuple[float, float] = (37.7749, -122.4194)
metadata: Dict[str, Union[str, int]] = {"model": "claude-opus-5", "tokens": 1000000}
middle_name: Optional[str] = None
Functions are where type hints shine brightest. Always type your arguments and your return values. Use -> None for functions that perform side effects without returning data.
from typing import Callable, Sequence
def calculate_cost(tokens: int, rate_per_million: float) -> float:
"""Calculate API cost based on token usage."""
return (tokens / 1_000_000) * rate_per_million
def process_batch(items: Sequence[str], callback: Callable[[str], None]) -> None:
"""Process a sequence of items and execute a callback on each."""
for item in items:
callback(item)
import anthropic
def generate_completion(prompt: str, model_id: str = "claude-opus-5") -> str:
client = anthropic.Anthropic()
response = client.messages.create(
model=model_id,
max_tokens=4000,
messages=[{"role": "user", "content": prompt}],
)
return "".join(block.text for block in response.content if block.type == "text")
When your codebase grows, strict class hierarchies can slow you down. Use Protocol for structural subtyping (duck typing with type safety) and TypedDict for structured JSON payloads.
from typing import Protocol, TypedDict
class LLMProvider(Protocol):
def generate(self, prompt: str) -> str:
...
class APIConfig(TypedDict):
model: str
max_tokens: int
thinking_enabled: bool
def configure_request(config: APIConfig) -> None:
print(f"Configuring request for model: {config['model']}")
my_config: APIConfig = {
"model": "claude-sonnet-5",
"max_tokens": 8000,
"thinking_enabled": True
}
configure_request(my_config)
mypy --strict your_module.py to your CI/CD pipeline to catch type errors before they hit production.list[str] over List[str] and str | None over Optional[str] if you are running Python 3.10+.Any temporarily—just don't leave it there permanently.
What is your favorite advanced typing pattern in Python? Drop a comment below with your go-to trick or headache. If you found this cheat sheet helpful, hit the bookmark button and share it with your team!