← Back to Python series
🚀
Advanced
Annotations · TypeVar · Generic · dataclass · Protocol

Week 1 — Type Hints & Dataclasses

Add static type information to Python code with type hints, use TypeVar for generic functions, model data with @dataclass, and define structural subtypes with Protocol.

typingtype hintsdataclassProtocolGeneric
Duration
2.5 hours
Level
📊 Advanced
Prerequisite
🎯 Intermediate complete
OUTCOME
Write a type-annotated data pipeline that passes mypy --strict

What you'll learn

  • 1Annotate function signatures and variables
  • 2Use Optional, Union, List, Dict, Tuple from typing
  • 3Write generic functions with TypeVar
  • 4Model immutable records with frozen dataclasses
  • 5Define structural contracts with Protocol

1. Type Hints

python
from typing import Optional, Union

def greet(name: str, times: int = 1) -> str:
    return (name + "\n") * times

def find_user(user_id: int) -> Optional[dict]:
    db = {1: {"name": "Alice"}}
    return db.get(user_id)

# Union (Python 3.10+: use | directly)
def stringify(value: Union[int, float, str]) -> str:
    return str(value)
ℹ️

Type hints don't affect runtime behaviour — Python still runs without them. Use mypy or pyright for static checking.

2. Generic Functions

python
from typing import TypeVar, List

T = TypeVar("T")

def first(lst: List[T]) -> Optional[T]:
    return lst[0] if lst else None

print(first([1, 2, 3]))       # 1  — inferred as int
print(first(["a", "b"]))     # "a" — inferred as str

3. Dataclasses

python
from dataclasses import dataclass, field
from typing import List

@dataclass(frozen=True)  # immutable
class Point:
    x: float
    y: float

@dataclass
class Team:
    name: str
    members: List[str] = field(default_factory=list)

    def add(self, member: str) -> None:
        self.members.append(member)

t = Team("Alpha")
t.add("Alice")
t.add("Bob")
print(t)  # Team(name='Alpha', members=['Alice', 'Bob'])

4. Protocol

python
from typing import Protocol, runtime_checkable

@runtime_checkable
class Drawable(Protocol):
    def draw(self) -> str: ...

class Circle:
    def draw(self) -> str: return "O"

class Square:
    def draw(self) -> str: return "[]"

def render(shape: Drawable) -> None:
    print(shape.draw())

render(Circle())   # O
render(Square())   # []

💻 Examples

Run these examples and check the output yourself.

01_typed_pipeline.pyType-annotated data processing pipeline
CODE
from dataclasses import dataclass
from typing import List, Optional

@dataclass
class Record:
    name: str
    score: float
    active: bool = True

def load(raw: List[dict]) -> List[Record]:
    return [Record(**r) for r in raw]

def top_n(records: List[Record], n: int = 3) -> List[Record]:
    active = [r for r in records if r.active]
    return sorted(active, key=lambda r: r.score, reverse=True)[:n]

raw = [
    {"name": "Alice", "score": 92.5},
    {"name": "Bob",   "score": 87.0},
    {"name": "Carol", "score": 95.1, "active": False},
    {"name": "Dave",  "score": 88.3},
]
records = load(raw)
for r in top_n(records):
    print(f"{r.name}: {r.score}")
▶ Output
Alice: 92.5
Dave: 88.3
Bob: 87.0

📝 Exercises

Try them yourself first, then open the solution to compare.

Exercise 1

Type-safe Stack

Goal: Implement a generic Stack[T] class that passes mypy.

Requirements
  • Generic[T] base class
  • push(item: T), pop() -> T, peek() -> T, is_empty() -> bool
  • Raises IndexError on pop/peek from empty stack
  • Fully annotated
Toggle solution
SOLUTION
from typing import Generic, TypeVar, List

T = TypeVar("T")

class Stack(Generic[T]):
    def __init__(self) -> None:
        self._data: List[T] = []

    def push(self, item: T) -> None:
        self._data.append(item)

    def pop(self) -> T:
        if self.is_empty(): raise IndexError("Stack is empty")
        return self._data.pop()

    def peek(self) -> T:
        if self.is_empty(): raise IndexError("Stack is empty")
        return self._data[-1]

    def is_empty(self) -> bool:
        return len(self._data) == 0

s: Stack[int] = Stack()
s.push(1); s.push(2); s.push(3)
print(s.pop(), s.pop())
▶ Output
3 2
Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub ↗