We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Interfaces with Protocols and ABCs step 8 of 26
Fix the Variance of a Serializer and a Parser
You met variance in the type hints track with list[Dog] and with producer
and consumer classes. Here the same rule applies to a protocol's type
parameter.
A serializer consumes a value and returns a string. A parser takes a string and produces a value. The starter's program already works, and every test passes. mypy still reports five errors, and the first two name the problem:
L7:1 [misc]
Invariant type variable "T" used in protocol where contravariant one is expected
L11:1 [misc]
Invariant type variable "T" used in protocol where covariant one is expected
Why one type variable cannot serve both protocols
The starter declares T = TypeVar("T") once and uses it in both protocols. A
plain TypeVar is invariant, but the two protocols need opposite directions:
Serializer[T]takes aTindump(value: T). A serializer that accepts anyobjectcan serialize anint, so aSerializer[object]can go where code expects aSerializer[int]. That is contravariance.Parser[T]returns aTfromload. Everyintis anobject, so aParser[int]can go where code expects aParser[object]. That is covariance.
The other three errors are in solve, which relies on both substitutions.
What to write
Declare each protocol's type parameter in its class header:
class Serializer[T](Protocol):
def dump(self, value: T) -> str: ...
class Parser[T](Protocol):
def load(self, raw: str) -> T: ...
With this syntax, mypy infers each parameter's variance from how the class uses it. Then:
- Delete the module-level
T = TypeVar("T")and theTypeVarimport. - Give
roundtripits own type parameter, asdef roundtrip[T](value: T, serializer: Serializer[T], parser: Parser[T]) -> T:. Keep its body,parser.load(serializer.dump(value)). - Leave
ReprSerializer,IntParser,BoolParser,parse_allandsolveas they are. Do not addcastorAny.
Each [T] declares a new parameter that belongs to that class or function
alone, even though they share a letter.
What the driver returns
solve(ints, flags, raws) returns one list of strings, in three groups:
- Each integer, serialized with
repr, parsed back withint, and shown withrepr. - Each boolean, through the same serializer and
BoolParser, as"True"or"False". - Each string in
raws, parsed byIntParserand shown withrepr. So"007"becomes"7", and"-0"becomes"0".
For ints=[-3, 0], flags=[False] and raws=["007", "-0"], it returns
["-3", "0", "False", "7", "0"].
Older code spells the same fix with two variables, such as
TypeVar("T_contra", contravariant=True) and
TypeVar("T_co", covariant=True).
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.