Typing
Annotations describe what a function expects. Python does not enforce them at runtime. Tools such as type checkers and editors read them, and so do people.
def lengths(
words: list[str],
) -> dict[str, int]:
return {w: len(w) for w in words}list[str],dict[str, int]andtuple[int, int]describe contents.int | Nonemeans either one.type UserId = intcreates a named alias.
Generic functions
A type parameter links an input type to an output type:
def last[T](items: list[T]) -> T:
return items[-1]Now a checker knows that last(["a"]) is a str.
Lazy annotations
Since Python 3.14, annotations are evaluated only when something asks for them, so a class can name itself in its own methods without quotes. typing.get_type_hints(fn) returns the evaluated annotations.
Your task
Add exact annotations: word_lengths(words: list[str]) -> dict[str, int], and make first_or_none generic with a type parameter T, taking items: list[T] and returning T | None. Define the alias type Scores = dict[str, int]. Fix first_or_none so that it returns None for an empty list.
The explanation opens here once every check passes.