Lesson 19 · Python standard library deep dive
Python typing & enum Explained: Type Hints Done Right | Standard Library #19
Video nineteen of the twenty-five-part series: typing, for type hints and static analysis, and enum, for real enumerations. Optional, Union, generics,…
- CoursePython standard library deep dive
- Lesson19 of 24
- Video18 min
- FormatJupyter notebook · 11 code cells
What you'll learn
Data
No separate download needed — the notebook creates or downloads everything it uses.
📓 Full notebook
Download .ipynbPython Standard Library Deep-Dive, Video 19: typing and enum#
- Video nineteen of the twenty-five-part series: typing, for type hints and static analysis, and enum, for real enumerations.
- Optional, Union, generics, Protocol, TypedDict, plus Enum, IntEnum, auto, and Flag.
- Let's get into it.
Part 1: Why Type Hints#
def add(a: int, b: int) -> int:
return a + b
print(add(2, 3))
print(add('a', 'b'))
print(add.__annotations__)
Part 2: Basic Type Hints - Variables, Parameters, Returns#
name: str = 'Ana'
age: int = 30
scores: list
scores = [85, 90, 78]
def greet(person: str, times: int = 1) -> str:
return (f'Hello, {person}! ' * times).strip()
print(greet('Sam', times=2))
Part 3: Optional and Union#
from typing import Optional, Union
def find_user(user_id: int) -> Optional[str]:
users = {1: 'Ana', 2: 'Sam'}
return users.get(user_id)
print(find_user(1))
print(find_user(99))
def parse_value(raw: str) -> Union[int, float, str]:
if raw.isdigit():
return int(raw)
try:
return float(raw)
except ValueError:
return raw
print(parse_value('42'), parse_value('3.14'), parse_value('hello'))
def modern_optional(value: int | None = None) -> str:
return 'none' if value is None else str(value)
print(modern_optional())
print(modern_optional(7))
Part 4: Generic Containers - list[int], dict[str, int], tuple#
def average(values: list[int]) -> float:
return sum(values) / len(values)
print(average([10, 20, 30]))
def word_lengths(words: list[str]) -> dict[str, int]:
return {w: len(w) for w in words}
print(word_lengths(['hi', 'there', 'friend']))
def coordinates() -> tuple[int, int]:
return (3, 4)
print(coordinates())
Part 5: Callable and Any#
from typing import Callable, Any
def apply_twice(func: Callable[[int], int], value: int) -> int:
return func(func(value))
print(apply_twice(lambda x: x * 2, 3))
def log_anything(value: Any) -> None:
print(f'logged: {value!r}')
log_anything(42)
log_anything('text')
log_anything([1, 2, 3])
Part 6: TypedDict#
from typing import TypedDict
class Movie(TypedDict):
title: str
year: int
rating: float
def describe(movie: Movie) -> str:
return f"{movie['title']} ({movie['year']}): {movie['rating']}/10"
m: Movie = {'title': 'Arrival', 'year': 2016, 'rating': 8.0}
print(describe(m))
print(type(m))
Part 7: Protocol - Structural Typing#
from typing import Protocol
class SupportsArea(Protocol):
def area(self) -> float: ...
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
class Square:
def __init__(self, side):
self.side = side
def area(self):
return self.side ** 2
def total_area(shapes: list[SupportsArea]) -> float:
return sum(shape.area() for shape in shapes)
print(total_area([Circle(2), Square(3)]))
Part 8: enum.Enum Basics#
from enum import Enum
class Status(Enum):
PENDING = 1
ACTIVE = 2
COMPLETED = 3
s = Status.ACTIVE
print(s)
print(s.name)
print(s.value)
print(s == Status.ACTIVE)
print(s == 2)
print(list(Status))
Part 9: IntEnum, auto(), and Flag#
from enum import IntEnum, auto, Flag
class Priority(IntEnum):
LOW = auto()
MEDIUM = auto()
HIGH = auto()
print(Priority.HIGH == 3)
print(Priority.HIGH > Priority.LOW)
class Permission(Flag):
READ = auto()
WRITE = auto()
EXECUTE = auto()
combined = Permission.READ | Permission.WRITE
print(combined)
print(Permission.READ in combined)
print(Permission.EXECUTE in combined)
Part 10: A Real Pattern - Enum-Driven State Machine#
class OrderState(Enum):
CREATED = 'created'
PAID = 'paid'
SHIPPED = 'shipped'
DELIVERED = 'delivered'
VALID_TRANSITIONS: dict[OrderState, list[OrderState]] = {
OrderState.CREATED: [OrderState.PAID],
OrderState.PAID: [OrderState.SHIPPED],
OrderState.SHIPPED: [OrderState.DELIVERED],
OrderState.DELIVERED: [],
}
def transition(current: OrderState, target: OrderState) -> OrderState:
if target not in VALID_TRANSITIONS[current]:
raise ValueError(f'cannot go from {current} to {target}')
return target
order = OrderState.CREATED
order = transition(order, OrderState.PAID)
print(order)
try:
transition(order, OrderState.DELIVERED)
except ValueError as e:
print(f'caught: {e}')
Wrap-Up: What You Learned#
- Type hints document expected types for tools and editors; Python itself never enforces them at runtime.
- Optional[X] and X | None both mean 'X or None'; Union expresses 'one of several types'.
- Built-in containers can be parameterized directly: list[int], dict[str, int], tuple[int, int].
- Callable[[Args], Return] hints function-shaped values; Any opts a value out of type checking entirely.
- TypedDict describes an expected dict shape; at runtime it's still just an ordinary dict.
- Protocol enables structural typing: any object satisfying the required methods qualifies, no inheritance needed.
- Enum creates a fixed, named set of distinct constant values, safer than magic strings or numbers.
- IntEnum members compare equal to plain ints; auto() assigns values automatically; Flag supports bitwise combination.
- A real pattern: combining Enum and type hints to model valid states and enforce legal transitions.
- That wraps up typing and enum. Next up: unittest, for writing and running real automated tests.
Found this useful?
All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.



