It’s not clear in PEP 695 whether private members of a class should be taken into account when variance of the class’s type-vars gets inferred. Pyright’s current implementation (v1.1.336) are using them all, but I personally think that they need to be excluded. What will be your thoughts?
For example –
class Translator[K, V]:
__dictionary: dict[K, V]
def __init__(self, dictionary: dict[K, V], /) -> None:
self.__dictionary = dictionary
def __getitem__(self, key: K, /) -> V:
return self.__dictionary[key]
I imagined that K
would be contravariant and V
would be covariant, because K
is only used as input and V
is only used as output. But in fact, K
and V
are both inferred as invariant, because __dictionary
is typed as dict[K, V]
and both arguments of dict
are invariant.
What’s worse, even after re-typing __dictionary
into collections.abc.Mapping
–
from collections.abc import Mapping
class Translator[K, V]:
__dictionary: Mapping[K, V]
def __init__(self, dictionary: Mapping[K, V], /) -> None:
self.__dictionary = dictionary
def __getitem__(self, key: K, /) -> V:
return self.__dictionary[key]
K
is still inferred as invariant instead of contravariant, because there are methods like keys
and items
on collections.abc.Mapping
which return the key type and make it invariant.
To make this sample work as I expected, I need to add a custom protocol, with only __getitem__
method which I’m going to use, to further restrict the type of __dictionary
. But I don’t think that should be required.