typeshed
typeshed copied to clipboard
Is there a good reason why `Mapping.__contains__` expects `object`, but `Mapping.get` expects a strict type?
I ran into a case where a user intended to check whether an enum value x
was present in a dictionary Dict[int, int]
: x.value in d
.
But instead they wrote x in d
, which always fails, since the Enum
itself is never a key in the dictionary. The type checker didn't complain since the typeshed stub for Mapping.__contains__
accepts any type compatible with object
:
def __contains__(self, __o: object) -> bool: ...
However, if the user had used d.get(x)
, the type checker would have complained, because the typeshed stub for Mapping.get
is stricter:
def get(self, __key: _KT) -> _VT_co | None: ...
Simple repro:
d: dict[int, int] = {1: 2}
# no type error
'hello' in d
# type error
d.get('hello')
It looks like the stub for __contains__
has expected object
ever since typing.pyi
was added to typeshed (in 2015). If the idea is that users may want to check for existence of keys of arbitrary types, a similar argument would hold for d.get(x)
.
Is there a good reason why the stub complains about d.get(x)
but not x in d
? Otherwise, it'd be good to make __contains__
stricter.
I definitely see the logic in that, but I also expect that a change like this would break quite a lot. An exploratory PR to judge the impact would be interesting, though.
This is a similar issue to #6597, though it comes at it from the opposite direction