How is `__hash__` typing supposed to work?
Do we have any docs or past discussions (I didn't find anything from a quick search) on the expected behavior of Hashable and `__hash__` typing rules. As far as I can tell, state of the art in typeshed is that: - object defines `__hash__` (I can see why, it's true at runtime for actual object types) - unhashable types explicitly "un-define" `__hash__` by setting a `ClassVar[None]` It appears to me that Mypy, Pyre, and Pyright both support this in the sense that they will type a list's `__hash__` attribute as `None`. But because upcasting list to object should be safe, all three type checkers say (a) you can do that and (b) `__hash__` is definitely not `None` once you do that. They diverge as to whether a list is a valid dict key (in the absence of upcasting); MyPy and Pyre both treat it as okay whereas Pyright says list is not hashable. I don't really have strong opinions about how this ought to work, but I'm a little surprised that I didn't easily find any discussion about both (a) declaring `object` to be hashable even though it has unhashable subtypes (b) the use of `ClassVar[None]` to declare incompatible overrides that introduce unsoundness My github PR search skills aren't great, so it may be this was discussed in typeshed PRs. It does look like at least in the past there was a decision not to really enforce hashability anyway at least in MyPy: https://github.com/python/typeshed/pull/6244. ------- Here are all three type checkers on a snippet illustrating: - directly asking about the type of `list.__hash__` (all of them say `None`) - passing `list` to a function taking an `object` and asking if `__hash__` is non-None (all of them say yes) - directly trying to create a dict with list keys (Mypy and Pyre both allow it, Pyright does not) Mypy: https://mypy-play.net/?mypy=latest&python=3.11&gist=174be7c81c486062cd662a1ceda01fac Pyright: https://pyright-playground.decorator-factory.su/?gzip=H4sIAD1mDmQC_2VPzQrCMA... Pyre: https://pyre-check.org/play?input=def%20takes_hashable(o%3A%20object)%20-%3E...)
As far as the historic question goes, I don't think we ever really discussed it. I suspect that in the PEP 484 days we all just assumed it was orthogonal to typing (as you've pretty much discovered) so we never even considered making it something that can be checked by a static type checker. If I had to guess about why pyright treats it differently, presumably Eric saw a way to be more useful to users. As to what *should* be done, maybe we shouldn't do anything? That PR was closed for a reason. On Sun, Mar 12, 2023 at 5:07 PM Steven Troxler <steven.troxler@gmail.com> wrote:
Do we have any docs or past discussions (I didn't find anything from a quick search) on the expected behavior of Hashable and `__hash__` typing rules.
As far as I can tell, state of the art in typeshed is that: - object defines `__hash__` (I can see why, it's true at runtime for actual object types) - unhashable types explicitly "un-define" `__hash__` by setting a `ClassVar[None]`
It appears to me that Mypy, Pyre, and Pyright both support this in the sense that they will type a list's `__hash__` attribute as `None`.
But because upcasting list to object should be safe, all three type checkers say (a) you can do that and (b) `__hash__` is definitely not `None` once you do that.
They diverge as to whether a list is a valid dict key (in the absence of upcasting); MyPy and Pyre both treat it as okay whereas Pyright says list is not hashable.
I don't really have strong opinions about how this ought to work, but I'm a little surprised that I didn't easily find any discussion about both (a) declaring `object` to be hashable even though it has unhashable subtypes (b) the use of `ClassVar[None]` to declare incompatible overrides that introduce unsoundness
My github PR search skills aren't great, so it may be this was discussed in typeshed PRs.
It does look like at least in the past there was a decision not to really enforce hashability anyway at least in MyPy: https://github.com/python/typeshed/pull/6244.
-------
Here are all three type checkers on a snippet illustrating: - directly asking about the type of `list.__hash__` (all of them say `None`) - passing `list` to a function taking an `object` and asking if `__hash__` is non-None (all of them say yes) - directly trying to create a dict with list keys (Mypy and Pyre both allow it, Pyright does not)
Mypy:
https://mypy-play.net/?mypy=latest&python=3.11&gist=174be7c81c486062cd662a1ceda01fac
Pyright:
https://pyright-playground.decorator-factory.su/?gzip=H4sIAD1mDmQC_2VPzQrCMA...
Pyre:
https://pyre-check.org/play?input=def%20takes_hashable(o%3A%20object)%20-%3E...) _______________________________________________ Typing-sig mailing list -- typing-sig@python.org To unsubscribe send an email to typing-sig-leave@python.org https://mail.python.org/mailman3/lists/typing-sig.python.org/ Member address: guido@python.org
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>
participants (2)
-
Guido van Rossum -
Steven Troxler