Python Basics: Type Hints
Learn modern type annotations, generics, Optional
/Union
, Callable
, TypedDict
, Protocol
, and best practices with static checkers.
Quiz progress
0 / 10 answered
Contents
What Type Hints Are (and Aren’t) #
Essentials:
- Hints add optional static types. Python doesn’t enforce them at runtime by default.
- Annotations live in
__annotations__
and are consumed by tools (mypy, pyright, IDEs). - Annotate function signatures and important variables for clarity and tooling.
- Forward references: use quotes (e.g.,
"User"
) orfrom __future__ import annotations
to postpone evaluation.
def area(r: float) -> float:
return 3.14159 * r * r
x: int = 3 # variable annotation
print(area(2.0))
print(__annotations__)
“Type hints document your intent and let tools catch mistakes—without changing Python’s runtime semantics.”
Quiz: Type Hint Basics
1. At runtime, type hints…
2. Choose the best signature for a function that takes a
float
and returns a float
:Collections & Generics #
Modern syntax (3.9+):
- Use built-in generics:
list[int]
,dict[str, int]
,set[str]
,tuple[int, str]
. - Type aliases:
UserId = int
,StrList = list[str]
. Any
= opt out;object
= most general supertype (accepts anything but you must narrow to use).
from typing import Any
names: list[str] = ["ada", "grace"]
scores: dict[str, int] = {"math": 95}
raw: Any = "could be anything"
top3: tuple[int, int, int] = (10, 9, 8)
Quiz: Collections & Generics
3. On Python 3.9+, idiomatic way to type a list of strings is…
4. Dict mapping course names to scores (ints):
Unions, Optional, Callable & Tuples #
Handy types:
Union[X, Y]
orX | Y
(3.10+)Optional[X]
meansX | None
Callable[[ArgTypes...], Return]
- Tuples:
tuple[int, str]
(fixed length) ortuple[int, ...]
(var-length)
from typing import Optional, Union, Callable
maybe_id: Optional[int] = None # same as int | None
text_or_id: Union[str, int] = "abc" # or: str | int
op: Callable[[int, int], int]
op = lambda a, b: a + b
pair: tuple[str, float] = ("price", 9.99)
Quiz: Unions, Optional, Callable & Tuples
5. Which are equivalent to “
int
or None
”? (Select all)6. Type for a function that takes two
int
s and returns a str
:7. Idiomatic (3.10+) type for a pair like
("price", 9.99)
is…Dataclasses, TypedDict & Protocol #
Structured typing options:
@dataclass
uses annotations to define fields and generate methods; types aren’t enforced automatically.TypedDict
: dicts with a typed shape (checked by type checkers; still a normaldict
at runtime).Protocol
: structural (duck) typing—match by methods/attributes, not inheritance.
from dataclasses import dataclass
from typing import TypedDict, Protocol
@dataclass
class User:
name: str
age: int = 0
class UserTD(TypedDict):
id: int
name: str
class Greeter(Protocol):
def greet(self, name: str) -> str: ...
def say_hello(g: Greeter) -> str:
return g.greet("Ada")
Quiz: Dataclasses, TypedDict & Protocol
8. Which is true about
@dataclass
?9. Which statement about
TypedDict
is correct?Best Practices #
Guidelines:
- Annotate function signatures and public APIs; key variables that aid readability.
- Prefer modern built-in generics (
list[int]
) on 3.9+. - Use
Any
sparingly—prefer precise unions or protocols. - Keep hints simple; don’t sacrifice readability for cleverness.
- Run a checker (mypy/pyright) in CI to get value from your hints.
Quiz: Best Practices
10. Which are good practices? (Select all)
Final Quiz & Summary #
Review your performance and revisit questions you missed.
Category: python · Lesson: type-hints-basics
Learning by Examples
Edit the code and see the result in the console panel.
# Experiment with type hints at runtime.
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Optional, Union, Callable, TypedDict, Protocol, get_type_hints
def greet(name: str, times: int = 1) -> str:
return " ".join([f"Hello, {name}!"] * times)
print("greet annotations:", get_type_hints(greet))
# Variables
count: int = 3
maybe_id: Optional[int] = None
price_pair: tuple[str, float] = ("price", 9.99)
# Callable
adder: Callable[[int, int], int] = lambda a, b: a + b
print("adder(2,3) =", adder(2,3))
# TypedDict
class UserTD(TypedDict):
id: int
name: str
u: UserTD = {"id": 1, "name": "Ada"}
u["id"] += 1
print("UserTD:", u)
# Protocol (duck typing)
class Greeter(Protocol):
def greet(self, name: str) -> str: ...
class Friendly:
def greet(self, name: str) -> str:
return f"Hi {name}"
def welcome(g: Greeter) -> str:
return g.greet("Grace")
print("welcome:", welcome(Friendly()))
# Dataclass picks up annotations for fields
@dataclass
class Point:
x: float
y: float = 0.0
p = Point(1.5)
print("Point:", p)
# Demonstrate that hints are not enforced automatically:
def expects_int(n: int) -> None:
print("n squared:", n*n)
expects_int(5) # ok
expects_int("5") # still runs in CPython; static tools would flag this