What Are Type Hints in Python
Type Hints (type annotations) are a system for specifying expected data types in Python code, helping improve readability, reliability, and software quality. While Python remains a dynamically typed language, explicitly declaring types helps catch many bugs during development.
Why Use Type Annotations
Python is known for its flexibility, but that flexibility often leads to hidden bugs from unexpected data types. Consider this example without annotations:
def add(a, b):
return a + b
print(add(5, 10)) # 15
print(add("5", "10")) # '510' — unexpected result
The expression add("5", "10") won't raise an error, but the result may surprise the developer.
Type Hints solve these problems:
- Explicit type declarations for arguments and return values
- Improved code readability for your development team
- Static analysis to catch errors before runtime
- Better IDE autocompletion with more accurate suggestions
- Self-documenting APIs without extra comments
Basic Type Hint Examples
Function Annotations
def add(a: int, b: int) -> int:
return a + b
def calculate_area(radius: float) -> float:
return 3.14159 * radius ** 2
def format_greeting(name: str) -> str:
return f"Hello, {name}!"
Variable Annotations
name: str = "Alice"
age: int = 30
pi: float = 3.14159
is_active: bool = True
Working with Collections
from typing import List, Dict, Tuple, Set
def process_scores(scores: List[int]) -> float:
return sum(scores) / len(scores)
def get_user_data() -> Dict[str, str]:
return {"name": "John", "email": "john@example.com"}
def get_coordinates() -> Tuple[float, float]:
return (10.5, 20.3)
def unique_values(items: List[str]) -> Set[str]:
return set(items)
Advanced Type Hint Features
Optional and Union
from typing import Optional, Union
def greet(name: Optional[str] = None) -> str:
if name:
return f"Hello, {name}!"
return "Hello, Guest!"
def process_value(value: Union[int, float, str]) -> str:
return str(value)
# In Python 3.10+ you can use the | operator
def process_new_syntax(value: int | float | str) -> str:
return str(value)
Callable Types
from typing import Callable
def apply_function(func: Callable[[int, int], int], a: int, b: int) -> int:
return func(a, b)
def multiply(x: int, y: int) -> int:
return x * y
result = apply_function(multiply, 5, 3) # 15
Generic Types
from typing import TypeVar, Generic, List
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:
if not self.items:
raise IndexError("Stack is empty")
return self.items.pop()
def is_empty(self) -> bool:
return len(self.items) == 0
# Usage
int_stack = Stack[int]()
int_stack.push(42)
Annotations in Classes
from typing import List, Optional
from dataclasses import dataclass
class Person:
def __init__(self, name: str, age: int, email: Optional[str] = None) -> None:
self.name = n