Type Hints and Annotations
Document your code with type hints for better clarity and tooling support.
Type hints (also called type annotations) are a way to indicate the expected types of function parameters, return values, and variables. They help make code more readable, enable better IDE support, and allow static type checking tools to catch errors.
Basic Type Hints
Section titled “Basic Type Hints”Function Annotations
Section titled “Function Annotations”def greet(name: str) -> str: """Function with type hints""" return f"Hello, {name}!"
def calculate_total(price: float, quantity: int) -> float: """Calculate total with type hints""" return price * quantity
# Usageresult = greet("Alice") # IDE knows result is strtotal = calculate_total(19.99, 3) # IDE knows total is floatVariable Annotations
Section titled “Variable Annotations”# Variable type hintsusername: str = "alice"age: int = 25price: float = 19.99is_active: bool = True
# Type hints help IDEs provide better autocomplete and error detectionCommon Type Hints
Section titled “Common Type Hints”Built-in Types
Section titled “Built-in Types”def process_data( name: str, age: int, height: float, is_student: bool, score: float | None = None # Optional (Python 3.10+)) -> str: """Function with various type hints""" status = "student" if is_student else "not a student" score_text = f" with score {score}" if score else "" return f"{name}, age {age}, height {height}cm, {status}{score_text}"
# Usageresult = process_data("Alice", 25, 165.5, True, 95.5)Collections
Section titled “Collections”from typing import List, Dict, Tuple, Set
def process_items(items: List[str]) -> List[str]: """Process a list of strings""" return [item.upper() for item in items]
def get_user_data() -> Dict[str, int]: """Return a dictionary mapping strings to integers""" return {"alice": 25, "bob": 30}
def get_coordinates() -> Tuple[float, float]: """Return a tuple of two floats""" return (40.7128, -74.0060)
def unique_items(items: Set[str]) -> Set[str]: """Process a set of strings""" return {item.upper() for item in items}
# Python 3.9+ you can use built-in typesdef process_items_v2(items: list[str]) -> list[str]: """Using built-in list type (Python 3.9+)""" return [item.upper() for item in items]Optional and Union Types
Section titled “Optional and Union Types”Optional Values
Section titled “Optional Values”from typing import Optional
def find_user(user_id: int) -> Optional[str]: """Return username or None if not found""" users = {1: "alice", 2: "bob"} return users.get(user_id)
# Python 3.10+ syntaxdef find_user_v2(user_id: int) -> str | None: """Using | syntax (Python 3.10+)""" users = {1: "alice", 2: "bob"} return users.get(user_id)Union Types
Section titled “Union Types”from typing import Union
def process_value(value: Union[int, str]) -> str: """Accept either int or str""" return str(value)
# Python 3.10+ syntaxdef process_value_v2(value: int | str) -> str: """Using | syntax (Python 3.10+)""" return str(value)Class Type Hints
Section titled “Class Type Hints”Class Attributes
Section titled “Class Attributes”class User: """User class with type hints""" def __init__(self, username: str, email: str, age: int): self.username: str = username self.email: str = email self.age: int = age self.is_active: bool = True
def get_info(self) -> str: """Return user information""" status = "active" if self.is_active else "inactive" return f"{self.username} ({self.email}), Age: {self.age}, Status: {status}"
# Usageuser = User("alice", "alice@example.com", 25)print(user.get_info())Class Methods
Section titled “Class Methods”class Calculator: """Calculator with type hints"""
@staticmethod def add(a: float, b: float) -> float: """Add two numbers""" return a + b
@classmethod def multiply(cls, a: float, b: float) -> float: """Multiply two numbers""" return a * b
def divide(self, a: float, b: float) -> float: """Divide two numbers""" if b == 0: raise ValueError("Cannot divide by zero") return a / bAdvanced Type Hints
Section titled “Advanced Type Hints”Generic Types
Section titled “Generic Types”from typing import TypeVar, Generic, List
T = TypeVar('T') # Generic type variable
class Stack(Generic[T]): """Generic stack class""" def __init__(self): self.items: List[T] = []
def push(self, item: T) -> None: """Push item onto stack""" self.items.append(item)
def pop(self) -> T: """Pop item from stack""" return self.items.pop()
def is_empty(self) -> bool: """Check if stack is empty""" return len(self.items) == 0
# Usageint_stack = Stack[int]()int_stack.push(1)int_stack.push(2)
str_stack = Stack[str]()str_stack.push("hello")str_stack.push("world")Callable Types
Section titled “Callable Types”from typing import Callable
def apply_operation( a: float, b: float, operation: Callable[[float, float], float]) -> float: """Apply a function to two numbers""" return operation(a, b)
def add(x: float, y: float) -> float: return x + y
def multiply(x: float, y: float) -> float: return x * y
# Usageresult1 = apply_operation(5, 3, add) # 8result2 = apply_operation(5, 3, multiply) # 15Real-World Example: API Client
Section titled “Real-World Example: API Client”from typing import Dict, List, Optional, Anyfrom dataclasses import dataclass
@dataclassclass User: """User data class with type hints""" id: int username: str email: str age: int
class APIClient: """API client with comprehensive type hints"""
def __init__(self, base_url: str, api_key: str): self.base_url: str = base_url self.api_key: str = api_key
def get_user(self, user_id: int) -> Optional[User]: """Fetch a user by ID""" # API call logic here return User(id=user_id, username="alice", email="alice@example.com", age=25)
def get_all_users(self) -> List[User]: """Fetch all users""" # API call logic here return [ User(id=1, username="alice", email="alice@example.com", age=25), User(id=2, username="bob", email="bob@example.com", age=30) ]
def create_user(self, user_data: Dict[str, Any]) -> User: """Create a new user""" # API call logic here return User( id=user_data.get("id", 0), username=user_data["username"], email=user_data["email"], age=user_data["age"] )
# Usageclient = APIClient("https://api.example.com", "api_key_123")user = client.get_user(1)users = client.get_all_users()Type Checking
Section titled “Type Checking”Type hints don’t affect runtime behavior, but you can use tools like mypy to check types:
# Install mypypip install mypy
# Check your codemypy your_file.pyExample with Type Errors
Section titled “Example with Type Errors”def add_numbers(a: int, b: int) -> int: return a + b
# These would be caught by mypy:# result = add_numbers("hello", "world") # Type error!# result = add_numbers(5, "world") # Type error!result = add_numbers(5, 3) # OK