We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Precise Type Hints with Literals and Generics step 14 of 26
Why a list[Dog] Is Not a list[Animal]
The starter's summarise counts values across a batch of records, and its
signature takes list[dict[str, object]]. Two callers hold records of a
narrower type, a list[dict[str, str]] and a tuple[dict[str, int], ...],
and mypy rejects both calls. Fix the signature, not the callers.
Why a list of dogs is not a list of animals
Say Dog is a subclass of Animal. Then Dog is a subtype of Animal,
which means a Dog can go anywhere an Animal is expected. A list[Dog] cannot
go everywhere a list[Animal] can, though:
def adopt(pets: list[Animal]) -> None:
pets.append(Cat())
dogs: list[Dog] = [Dog()]
adopt(dogs)
error: Argument 1 to "adopt" has incompatible type "list[Dog]"; expected "list[Animal]"
note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
note: Consider using "Sequence" instead, which is covariant
A function that takes list[Animal] may append any animal, such as a cat. If
mypy allowed the call, your list of dogs would hold a cat, and the next
dogs[-1].bark() would crash. So list is invariant, which means
list[Dog] cannot be used as list[Animal], or the other way round. dict
is invariant in its value type for the same reason, because you can store a
new value in it.
Sequence from collections.abc has no append and no item assignment.
Sequence is covariant, which means it follows the element type.
Sequence[Dog] can be used as Sequence[Animal], because code can only read
from it. Mapping is the read-only version of dict. It is covariant in its
value type, so Mapping[str, str] can be used as Mapping[str, object]. Its
key type stays invariant.
The name for these rules is variance. A third kind, contravariance, goes the other way and applies to function parameters. Later exercises in this track cover it.
What to write
from collections.abc import Mapping, Sequence
def summarise(records: Sequence[Mapping[str, object]]) -> dict[str, int]:
...
Change both layers of the type. Sequence[dict[str, object]] still fails,
because dict is invariant in its values. Keep the two constants and their
calls in solve as they are, with no casts, so mypy checks your signature
against them.
For each key, summarise counts how many records hold a value other than
None:
- Visit the records in order, and each record's keys in order.
- Only
Nonecounts as absent.0,False,""and[]all count. - List the keys in the order you first see them, even when that first value
is
None. - A key seen only with
Noneappears with the count0. - Return a new dictionary, and leave the records unchanged.
summarise(({"a": 0, "b": None}, {"a": None, "b": False})) # {"a": 1, "b": 1}
list(summarise(({"z": None}, {"a": 1}))) # ["z", "a"]
What solve returns
The tests send each record as a list of [key, value] pairs, because the
site's JSON storage does not keep the key order of an object. Rebuild each
record with {str(key): value for key, value in record}.
If two keys become the same string, the dictionary keeps the last value at the
first key's position, and summarise counts that entry once. For example,
[[1, "first"], ["1", None]] becomes {"1": None}, which counts as 0.
Call summarise on the rebuilt records, and return a tuple of three lists:
- The keys, in first-seen order.
- Their counts, in the same order.
- The values of
summarise(NARROW_RECORDS), then the values ofsummarise(WIDE_RECORDS). These are always[2, 1, 2], even for empty input.
solve([[["a", None], ["b", 0]], [["a", 1]]]) # (["a", "b"], [1, 1], [2, 1, 2])
solve([[["a", 1], ["a", None]]]) # (["a"], [0], [2, 1, 2])
solve([]) # ([], [], [2, 1, 2])
The keys and counts come back as two lists so that the tests can check their order. See mypy's guide to variance for more examples.
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.