PEP 484: Type Hints - Static Type Checking for Python
TL;DR
PEP 484 introduced type hints to Python, enabling static type checking with tools like mypy while maintaining runtime flexibility and improving code documentation and IDE support.
Interesting!
Type hints are completely optional and ignored at runtime - Python remains dynamically typed, but type checkers can catch potential bugs before your code even runs!
Basic Type Hints
python code snippet start
def greet(name: str, age: int) -> str:
return f"Hello {name}, you are {age} years old"
# Variables can be annotated too
count: int = 0
message: str = "Hello, World!"
is_active: bool = True
# Function with no return value
def log_message(message: str) -> None:
print(f"LOG: {message}")
python code snippet end
Collection Types
python code snippet start
from typing import List, Dict, Set, Tuple, Optional
# Collection types
numbers: List[int] = [1, 2, 3, 4]
user_data: Dict[str, int] = {"age": 30, "score": 95}
tags: Set[str] = {"python", "typing", "pep484"}
coordinates: Tuple[float, float] = (3.14, 2.71)
# Optional types (can be None)
def find_user(user_id: int) -> Optional[str]:
users = {1: "Alice", 2: "Bob"}
return users.get(user_id) # Returns str or None
python code snippet end
Union Types and Generics
python code snippet start
from typing import Union, Generic, TypeVar
# Union types - can be one of several types
def process_id(user_id: Union[int, str]) -> str:
return str(user_id)
# Generic types
T = TypeVar('T')
def first_item(items: List[T]) -> Optional[T]:
return items[0] if items else None
# Usage preserves type information
first_number = first_item([1, 2, 3]) # Type: Optional[int]
first_word = first_item(["a", "b"]) # Type: Optional[str]
python code snippet end
Class Type Hints
python code snippet start
from typing import ClassVar
from datetime import datetime
class User:
# Class variable
total_users: ClassVar[int] = 0
def __init__(self, name: str, email: str) -> None:
self.name: str = name
self.email: str = email
self.created_at: datetime = datetime.now()
User.total_users += 1
def get_info(self) -> Dict[str, str]:
return {
"name": self.name,
"email": self.email
}
@classmethod
def create_admin(cls, name: str) -> 'User':
return cls(name, f"{name}@admin.com")
python code snippet end
Advanced Type Hints
python code snippet start
from typing import Callable, Protocol, Literal
# Function types
def apply_operation(x: int, operation: Callable[[int], int]) -> int:
return operation(x)
# Usage
result = apply_operation(5, lambda x: x * 2)
# Protocols (structural typing)
class Drawable(Protocol):
def draw(self) -> None: ...
def render(obj: Drawable) -> None:
obj.draw() # Any object with a draw() method works
# Literal types
def set_mode(mode: Literal["debug", "production"]) -> None:
print(f"Setting mode to {mode}")
set_mode("debug") # OK
set_mode("testing") # Type checker error
python code snippet end
Type Checking with mypy
bash code snippet start
# Install mypy
pip install mypy
# Check your code
mypy your_script.py
bash code snippet end
python code snippet start
# Example with type errors
def add_numbers(a: int, b: int) -> int:
return a + b
result = add_numbers("5", "10") # mypy error: incompatible types
python code snippet end
Type hints make Python code more self-documenting and help catch errors early in development.
Type hints evolved further with PEP 526 (Variable Annotations) and integrate seamlessly with Python's class system for comprehensive type safety. Modern Python extends type hints with built-in generic collections and union operator syntax for cleaner annotations. Advanced typing features include positional-only parameters for more precise function signatures and structural subtyping with protocols for duck typing with type safety.
Reference: PEP 484 - Type Hints