We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Interfaces with Protocols and ABCs step 13 of 26
Type a Lookup with a Default Using Overloads
fetch(store, key) looks up a key in a mapping of strings, and callers can
pass a default for a missing key. What comes back depends on the call:
fetch(store, key)gives astr, orNonewhen the key is missing.fetch(store, key, 0)gives astr, or theintdefault.fetch(store, key, "?")always gives astr.
The starter has one signature that returns object. It fits every call but
tells callers nothing. Submit it, and mypy rejects the code that uses the
results:
L16:53 [attr-defined]
"object" has no attribute "upper"
L20:24 [attr-defined]
"object" has no attribute "lower"
One function, several signatures
An overload is one of several signatures for one function, each marked
@overload, so the return type can depend on the arguments. Write the
overloads first, with ... as their bodies:
@overload
def fetch(store: Mapping[str, str], key: str) -> str | None: ...
@overload
def fetch[T](store: Mapping[str, str], key: str, default: T) -> str | T: ...
Then write one ordinary implementation, with no decorator, that handles
every call at runtime. mypy checks each call against the overloads in order
and uses the first one that matches. T is the type of the default, as in the generic
functions of the type hints track. A "?" default makes the result
str | str, which is the same as str.
mypy checks the body of the implementation only against its own signature. So its parameters must accept both call forms, and its return type must cover the result of every overload.
What to write
Import overload from typing. Copy the two overloads above exactly, and
add one implementation after them. It returns:
- The stored string when the key exists, even an empty string.
- The default object itself when the key is missing.
Nonewhen the key is missing and the caller passed no default.
Do not change the mapping or the default. The store holds only strings, so
store.get(key) returns None only for a missing key. Do not write
value or default, because it treats a stored "" as missing. mypy
accepts that bug, and a hidden test catches it.
Your code must pass mypy --strict without # type: ignore or cast.
Check the result types
assert_type from typing does nothing at runtime, and mypy reports an
error when the type it infers differs from the one you wrote. With your
overloads, mypy accepts all four lines:
from typing import assert_type
values: dict[str, str] = {"name": "Ada"}
assert_type(fetch(values, "name"), str | None)
assert_type(fetch(values, "name", 0), str | int)
assert_type(fetch(values, "name", "?"), str)
assert_type(fetch(values, "name", None), str | None)
The last line shows that a None default keeps None in the result.
What the driver returns
Keep solve as it is. For each key, in order, it appends three strings,
one for each call form. The first is "MISS" or the stored string in upper
case. The second is the repr of the lookup with the integer fallback.
The third is the lookup with a "?" default, in lower case. For
store={"a": "X"}, keys=["a", "missing"] and fallback=7, it returns
["X", "'X'", "x", "MISS", "7", "?"].
The starter already returns the right strings, so mypy is what this exercise grades. See function overloading in the mypy documentation.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Python reference solution
Sign in to track your attempts toward unlocking the reference solution.