We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Getting Started with Tests and Type Checks step 8 of 13
Placement 1/6: Make a Generic Class Read-Only
Solve this without the hints and you can skim the type hints track.
Frame[T] is a generic class. T is a type parameter, a placeholder
for the type of the rows, so a Frame[int] holds integers and a Frame[str]
holds strings. The function summarise takes a Frame[object] and only reads
it. Every int is an object, so int is a subtype of object. You
would expect summarise to accept a Frame[int], but mypy rejects the call:
Argument 1 to "summarise" has incompatible type "Frame[int]";
expected "Frame[object]" [arg-type]
The reason is append. If summarise could treat a Frame[int] as a
Frame[object], it could append a string to a frame of integers. So mypy
makes Frame invariant, which means Frame[int] and Frame[object] are
unrelated types. Your job is to make it covariant. Then, because int is
a subtype of object, Frame[int] is a subtype of Frame[object]. mypy can
make a class covariant only when nothing can put a new T into it once the
constructor has run.
What to write
Change only the class Frame. Leave summarise and solve as they are,
because they are the caller mypy must accept.
- Keep the
class Frame[T]syntax. With it, mypy works out the variance ofTfrom how the class usesT. - Remove the
appendmethod. - The constructor still takes a
Sequence[T]. Store the rows inself._rows: tuple[T, ...], a tuple of any length whose items are allT. Keep their order and any duplicates. rows()returns that tuple.- Copy the input. If the caller changes their list after building the frame, the frame stays the same.
- Keep
Tthroughout. Don't replace it withAnyorobject, and don't silence the error with# type: ignoreorcast.
Keep the leading underscore in _rows. A list keeps T invariant because it
can change in place. So does a public attribute, because any caller can
reassign it. mypy treats an attribute whose name starts with _ as private, and ignores
outside writes to it when it works out the variance.
What the driver reports
solve(ints, names) builds a Frame[int] and a Frame[str] and passes each
one to summarise. It returns the integer frame's lines, then the string
frame's. Each line is "index:value". The index restarts at 0 for each
frame, and the value is repr(row), so strings keep their quotes.
solve([1, 2], ["a"]) # ["0:1", "1:2", "0:'a'"]
solve([], []) # []
The starter already returns these values. It fails only the type check, so mypy grades your change.
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.