Type Hints & Validation¶
Overview¶
Type hints improve code clarity and enable tooling:
- Type annotations:
def func(x: int) -> str: - Generic types:
List[int],Dict[str, float] - Optional and Union:
Optional[int],Union[int, str] - Type checking: mypy, pyright validate types
-
Basic Type Hints¶
Function Annotations¶
# Simple types
def add(a: int, b: int) -> int:
return a + b
# Optional types
def greet(name: str = None) -> None:
if name:
print(f"Hello, {name}")
else:
print("Hello")
# Union types
from typing import Union
def process(value: Union[int, str]) -> str:
return str(value)
# Multiple returns
def divide(a: float, b: float) -> Union[float, str]:
if b == 0:
return "Error"
return a / b
Collection Types¶
from typing import List, Dict, Tuple, Set
def process_numbers(numbers: List[int]) -> Dict[str, int]:
return {
'sum': sum(numbers),
'count': len(numbers),
'max': max(numbers)
}
def coordinates() -> Tuple[float, float]:
return (1.0, 2.0)
def unique_tags(articles: List[Dict[str, str]]) -> Set[str]:
tags = set()
for article in articles:
if 'tags' in article:
tags.update(article['tags'].split(','))
return tags
Generic Types¶
TypeVar for Generic Functions¶
from typing import TypeVar, List
T = TypeVar('T') # Generic type variable
def get_first(items: List[T]) -> T:
"""Generic function - works with any list type."""
return items[0]
# Works with different types
numbers = get_first([1, 2, 3]) # Type is int
strings = get_first(['a', 'b', 'c']) # Type is str
Generic Classes¶
from typing import Generic, TypeVar
T = TypeVar('T')
class Stack(Generic[T]):
"""Generic stack."""
def __init__(self):
self._items: List[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
# Use it
int_stack: Stack[int] = Stack()
int_stack.push(1)
int_stack.push(2)
print(int_stack.pop()) # 2
Type Checking with mypy¶
Running mypy¶
# Install mypy
pip install mypy
# Check file
mypy script.py
# Check with strict mode
mypy --strict script.py
Type Checking Examples¶
# mypy checks these
def greet(name: str) -> str:
return f"Hello, {name}"
# Correct
result = greet("Alice")
# Wrong - mypy reports error
result = greet(42) # Error: Argument 1 has incompatible type "int"; expected "str"
Protocols: Structural Subtyping¶
Protocol Definition¶
from typing import Protocol
class Drawable(Protocol):
"""Protocol for drawable objects."""
def draw(self) -> None:
...
class Circle:
def draw(self) -> None:
print("Drawing circle")
class Square:
def draw(self) -> None:
print("Drawing square")
def render(obj: Drawable) -> None:
"""Works with any object that has draw() method."""
obj.draw()
# Works with Circle and Square (no inheritance needed!)
render(Circle()) #
render(Square()) #
ML Type Hints¶
PyTorch Model Typing¶
from typing import Tuple, Optional
import torch
import torch.nn as nn
def forward_pass(
model: nn.Module,
x: torch.Tensor,
training: bool = False
) -> torch.Tensor:
"""Type-hinted forward pass."""
if training:
model.train()
else:
model.eval()
with torch.no_grad():
return model(x)
def train_step(
model: nn.Module,
x: torch.Tensor,
y: torch.Tensor,
optimizer: torch.optim.Optimizer,
loss_fn: nn.Module
) -> Tuple[torch.Tensor, float]:
"""Type-hinted training step."""
optimizer.zero_grad()
logits = model(x)
loss = loss_fn(logits, y)
loss.backward()
optimizer.step()
return logits, loss.item()
DataLoader Typing¶
from typing import Iterator, Tuple
from torch.utils.data import DataLoader, TensorDataset
def create_dataloader(
X: torch.Tensor,
y: torch.Tensor,
batch_size: int = 32
) -> DataLoader:
"""Create typed dataloader."""
dataset = TensorDataset(X, y)
return DataLoader(dataset, batch_size=batch_size, shuffle=True)
def iterate_batches(
dataloader: DataLoader
) -> Iterator[Tuple[torch.Tensor, torch.Tensor]]:
"""Iterate with type hints."""
for batch_x, batch_y in dataloader:
yield batch_x, batch_y
TypedDict for Configuration¶
Typed Dictionaries¶
from typing import TypedDict
class ConfigDict(TypedDict):
"""Type-safe configuration dictionary."""
host: str
port: int
debug: bool
timeout: float
def load_config(path: str) -> ConfigDict:
"""Load configuration with type hints."""
import json
with open(path) as f:
config = json.load(f)
return config
# mypy ensures correct structure
config: ConfigDict = load_config('config.json')
print(config['host']) # OK
print(config['unknown']) # Error: Unknown key
Summary: Type Hints Benefits¶
| Benefit | Example |
|---|---|
| IDE support | Autocomplete works better |
| Error detection | mypy catches mistakes |
| Self-documenting | Types show intent |
| Refactoring | Rename safely |
-
Related Topics¶
- [01 Type System & Annotations](/05-py3/01-fundamentals/(01-type-system-annotations/) - Basic type hints
- [02 Execution Model & Compilation](/05-py3/09-bytecode-and-execution/(02-execution-model-compilation/) - Runtime behavior