Lab 07: Type Hints & Generics

Objective

Write fully type-annotated Python: typing module, generics, TypeVar, Protocol, TypedDict, Literal, overload, Final, and structural subtyping.

Time

30 minutes

Prerequisites

  • Lab 01 (Advanced OOP)

Tools

  • Docker image: zchencow/innozverse-python:latest


Lab Instructions

Step 1: Core Type Hints

docker run --rm zchencow/innozverse-python:latest python3 -c "
from typing import Optional, Union, Any, Callable, Sequence, Mapping
from collections.abc import Iterator, Generator, AsyncIterator

# Basic annotations
def greet(name: str, times: int = 1) -> str:
    return (f'Hello, {name}! ' * times).strip()

# Optional (= X | None)
def find_product(product_id: int, catalog: dict[int, str]) -> Optional[str]:
    return catalog.get(product_id)

# Union (pre-3.10 style, or use X | Y in 3.10+)
def process(value: Union[int, str, list[int]]) -> str:
    if isinstance(value, int): return f'int:{value}'
    if isinstance(value, str): return f'str:{value}'
    return f'list:{sum(value)}'

# Complex types
def batch_process(
    items: list[dict[str, Any]],
    transform: Callable[[dict[str, Any]], dict[str, Any]],
    filters: list[Callable[[dict[str, Any]], bool]] | None = None,
) -> list[dict[str, Any]]:
    result = items
    if filters:
        for f in filters: result = [x for x in result if f(x)]
    return [transform(x) for x in result]

catalog = {1: 'Surface Pro', 2: 'Surface Pen', 3: 'Office 365'}
print(greet('Dr. Chen', 2))
print(find_product(1, catalog))
print(find_product(99, catalog))
print(process(42))
print(process('hello'))
print(process([1, 2, 3]))

products = [
    {'id': 1, 'name': 'Surface Pro',  'price': 864.0,  'stock': 15},
    {'id': 2, 'name': 'Surface Pen',  'price': 49.99,  'stock': 80},
    {'id': 3, 'name': 'USB-C Hub',    'price': 29.99,  'stock': 0},
]
result = batch_process(
    products,
    lambda p: {**p, 'value': p['price'] * p['stock']},
    filters=[lambda p: p['stock'] > 0]
)
for r in result:
    print(f'  {r[\"name\"]}: value=\${r[\"value\"]:.2f}')
"

💡 Type hints are not enforced at runtime — they're metadata for type checkers (mypy, pyright) and IDEs. Use them to document intent and catch bugs at development time. Optional[X] is shorthand for Union[X, None]. In Python 3.10+, use X | None directly.

📸 Verified Output:


Step 2: TypeVar & Generics

📸 Verified Output:


Steps 3–8: TypedDict, Literal, Protocol, Final, NewType, Capstone

📸 Verified Output:


Summary

Type
Purpose
Example

TypeVar

Generic placeholder

T = TypeVar('T')

Generic[T]

Generic class

class Stack(Generic[T])

TypedDict

Typed dictionaries

class P(TypedDict): id: int

Literal

Restrict to values

Literal['asc', 'desc']

Protocol

Structural typing

class Serializable(Protocol)

Final

Runtime constant

MAX: Final = 10

NewType

Nominal alias

UserID = NewType('UserID', int)

overload

Multiple signatures

@overload def f(x: int)

Further Reading

Last updated