Type hints make Python code clearer and catch bugs before runtime. Here's how to use them effectively.
Basic Annotations
def greet(name: str) -> str:
return f"Hello, {name}"
age: int = 25
prices: list[float] = [9.99, 14.99, 29.99]
config: dict[str, int] = {"timeout": 30, "retries": 3}Types go after colons for variables, after arrows for return types.
Common Types
from typing import Optional, Union, Any
# Basic types
x: int = 1
y: float = 3.14
z: str = "hello"
flag: bool = True
# Collections (Python 3.9+)
items: list[str] = ["a", "b"]
mapping: dict[str, int] = {"key": 1}
coords: tuple[float, float] = (1.0, 2.0)
unique: set[int] = {1, 2, 3}
# Optional - can be None
user: Optional[str] = None # Same as str | None
# Union - multiple types
value: Union[int, str] = 42 # Same as int | str (3.10+)
# Any - escape hatch
data: Any = get_unknown_data()Function Signatures
from typing import Callable
# Basic function
def add(a: int, b: int) -> int:
return a + b
# Function returning None
def log(message: str) -> None:
print(message)
# Function as parameter
def apply(func: Callable[[int, int], int], x: int, y: int) -> int:
return func(x, y)
# Default arguments
def fetch(url: str, timeout: int = 30) -> str:
...
# *args and **kwargs
def variadic(*args: int, **kwargs: str) -> None:
...Classes and Methods
class User:
name: str
email: str
def __init__(self, name: str, email: str) -> None:
self.name = name
self.email = email
def greet(self) -> str:
return f"Hi, I'm {self.name}"
@classmethod
def from_dict(cls, data: dict[str, str]) -> "User":
return cls(data["name"], data["email"])Use string literals ("User") for forward references, or import from __future__ import annotations.
Generics
Create reusable typed containers:
from typing import TypeVar, Generic
T = TypeVar("T")
class Stack(Generic[T]):
def __init__(self) -> None:
self._items: list[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
# Usage
int_stack: Stack[int] = Stack()
int_stack.push(1)
int_stack.push("wrong") # Type error!TypeVar Bounds
Constrain generic types:
from typing import TypeVar
# Must be a number type
Number = TypeVar("Number", int, float)
def double(x: Number) -> Number:
return x * 2
# Must have specific method
from typing import Protocol
class Comparable(Protocol):
def __lt__(self, other: Any) -> bool: ...
C = TypeVar("C", bound=Comparable)
def minimum(a: C, b: C) -> C:
return a if a < b else bProtocols (Structural Typing)
Define interfaces without inheritance:
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> None: ...
class Circle:
def draw(self) -> None:
print("Drawing circle")
class Square:
def draw(self) -> None:
print("Drawing square")
def render(shape: Drawable) -> None:
shape.draw()
# Both work - they have draw()
render(Circle())
render(Square())TypedDict
Type dictionaries with known keys:
from typing import TypedDict
class Movie(TypedDict):
title: str
year: int
rating: float
movie: Movie = {
"title": "Inception",
"year": 2010,
"rating": 8.8
}Literal Types
Restrict to specific values:
from typing import Literal
def set_mode(mode: Literal["read", "write", "append"]) -> None:
...
set_mode("read") # OK
set_mode("delete") # Type errorUsing mypy
Install and run:
pip install mypy
mypy your_code.pyConfiguration in pyproject.toml:
[tool.mypy]
python_version = "3.11"
strict = true
warn_return_any = true
warn_unused_ignores = trueCommon flags:
--strict: Enable all strict checks--ignore-missing-imports: Skip untyped libraries--show-error-codes: Show error codes for targeted ignores
Type Ignore Comments
When you need to bypass checking:
# Ignore specific line
result = untyped_function() # type: ignore[no-untyped-call]
# Ignore entire file (at top)
# mypy: ignore-errorsUse sparingly—most ignores indicate fixable issues.
Gradual Adoption
Start with:
- Function signatures in new code
- Public API boundaries
- Complex functions where bugs hide
Skip:
- Simple scripts
- Test files (unless helpful)
- Prototype code
Quick Reference
from typing import (
Optional, # T | None
Union, # T | U
Any, # Escape hatch
Callable, # Function type
TypeVar, # Generic placeholder
Generic, # Base for generic classes
Protocol, # Structural typing
TypedDict, # Typed dictionaries
Literal, # Specific values
Final, # Constants
ClassVar, # Class-level variables
)
# Modern syntax (3.10+)
x: int | str # Union
y: list[int] | None # OptionalType hints are documentation that the computer can check. Use them to make your code clearer and catch bugs early.