17. Typing
Type hints let you describe the kind of values a function expects and returns. Python stays dynamically typed, but type hints make code easier to read and help static analysis tools catch mistakes.
17.1. Basic type hints
Here is a function with parameter and return annotations.
1def add(a: int, b: int) -> int:
2 return a + b
3
4
5result = add(2, 3)
6print(result)
17.2. Collection types
You can also annotate lists, dictionaries, and other container types.
1def average(numbers: list[float]) -> float:
2 return sum(numbers) / len(numbers)
3
4
5def word_counts(words: list[str]) -> dict[str, int]:
6 counts: dict[str, int] = {}
7 for word in words:
8 counts[word] = counts.get(word, 0) + 1
9 return counts
10
11
12print(average([2.0, 4.0, 6.0]))
13print(word_counts(['red', 'blue', 'red']))
17.3. Optional values
When a value may be missing, None is commonly part of the type.
1def find_user(user_id: int) -> str | None:
2 if user_id == 1:
3 return 'Jane'
4 return None
5
6
7print(find_user(1))
8print(find_user(2))
17.4. Typed aliases
Type aliases can make repeated type signatures easier to understand.
1type ScoresByUser = dict[str, list[int]]
2
3
4def top_score(data: ScoresByUser, user: str) -> int | None:
5 scores = data.get(user)
6 if not scores:
7 return None
8 return max(scores)
9
10
11print(top_score({'jane': [88, 93, 91]}, 'jane'))
12print(top_score({}, 'john'))
17.5. Why type hints help
Type hints are useful documentation. They show readers what shape of data the code expects, and they work well with tools such as type checkers, editors, and code review. They are most helpful when they clarify interfaces, not when they repeat the obvious everywhere.
17.6. Exercise
Annotate a small student-report module. Write:
a type alias for
dict[str, list[int]]to represent student gradesa function that returns the average for one student
a function that returns the top student name or
Noneif there is no data
Use type hints on all parameters and return values.