-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Mapping.values() return type is too narrow #4435
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
|
It is not a violation of Liskov if If |
I did some digging through git history and found that |
It seems reasonable to have something for values() that is not a set, since it may have duplicates in general. Can we make up a Protocol for this? |
Generally, in typeshed we prefer to annotate return types with the concrete type returned from the implementation, like we do in the See also #3576 for some previous discussion about |
Thanks for weighing in @gvanrossum, and great to hear you both think a protocol makes sense here too! In the meantime, implementors in my situation will have to use |
It doesn't seem like typeshed needs to change here. For the original post, sounds like you want a class that inherits from both ValuesView and AbstractSet. |
I thought this had been left open because we agreed a protocol would be better here, so that libraries like bidict wouldn't each have to reinvent their own such subclass. |
@JelleZijlstra et al., here is the sort of subclass I currently resort to in order to get around this issue: class BidictKeysView(typing.KeysView[KT], typing.ValuesView[KT]):
"""Since the keys of a bidict are the values of its inverse (and vice versa),
the ValuesView result of calling *bi.values()* is also a KeysView of *bi.inverse*.
""" But even with that, I still need to resort to def values(self) -> BidictKeysView[VT]:
"""A set-like object providing a view on the contained values.
Since the values of a bidict are equivalent to the keys of its inverse,
this method returns a set-like object for this bidict's values
rather than just a collections.abc.ValuesView.
This object supports set operations like union and difference,
and constant- rather than linear-time containment checks,
and is no more expensive to provide than the less capable
collections.abc.ValuesView would be.
See :meth:`keys` for more information.
"""
reveal_type(self.inverse.keys()) # Revealed type is "typing.KeysView[VT`2]"
return t.cast(BidictKeysView[VT], self.inverse.keys()) So it still seems like a protocol would be a better solution. What do you think? |
Currently, the type hint for
Mapping.values()
requires implementors of theMapping
interface to return aValuesView
:But
ValuesView
is too narrow. For example, an ABC for aBidirectionalMapping
interface which extends theMapping
interface should be able to declare that thevalues()
method returns anAbstractSet
(since the values of a bidirectional mapping actually make up a set), as I need to do in mybidict
library. Note that anAbstractSet
satisfies the contract of aValuesView
. But currently declaring this causes the mypy error,Return type "AbstractSet[VT]" of "values" incompatible with return type "ValuesView[VT]" in supertype "Mapping"
.(Notice that the type hints for
Mapping.keys()
andMapping.items()
returnAbstractSet
rather thanKeysView
andItemsView
, respectively, because evenKeysView
andItemsView
would have been too narrow there. In other words, those type hints are abstract enough. Only the type hint forvalues()
is not abstract enough.)It looks like one way to fix this would be to declare that
Mapping.values()
must return aCollection[VT]
rather than aValuesView[VT]
. SinceAbstractSet
is a subtype ofCollection
, thenBidirectionalMapping.values()
(and other implementors) could return anAbstractSet[VT]
(or any other subtype) and still satisfy the contract forMapping.values()
.Does that make sense, or would some other fix be better here?
The text was updated successfully, but these errors were encountered: