Repository navigation
Special-casing __hash__ #2284
Description
Activity
- addedtopic: typing specFor improving the typing specFor improving the typing spec
on May 12, 2026 There was a previous discussion about this at https://discuss.python.org/t/hash-eq-and-lsp/68138 .
Your proposal seems to be basically the status quo, where Hashable is a protocol defining the
__hash__method. A problem with that status quo is thatobjectis hashable, so a subclass that is not hashable is (correctly!) marked by type checkers as having an invalid override. You propose to allow that unsafe override. Mike's proposal in the linked thread was instead to say thatobjecthas__hash__of typeNone | () -> int, with a special case to allow instances of exactlyobjectto be hashable.Considering that the status quo is the status quo (and mirrors the implementation, i.e. classes are hashable by default), documenting this would be a big step forward.
Sub-classes inherit their parent's hashability, unless overridden.
This doesn't match runtime, as cpython removes
__hash__if a subclass implements__eq__, but not__hash__:>>> class Foo: ... ... >>> hash(Foo()) 8496821607350 >>> class Bar(Foo): ... def __eq__(self, other): return False ... >>> hash(Bar()) Traceback (most recent call last): File "<python-input-3>", line 1, in <module> hash(Bar()) ~~~~^^^^^^^ TypeError: unhashable type: 'Bar'
This is exactly what makes static typing for
__hash__so tricky..Reacted by Dave HalterThis doesn't match runtime, as cpython removes
__hash__if a subclass implements__eq__, but not__hash__:This is then something that type checkers also need to replicate and should be added to the list above, according to the philosophy "special casing in the Python interpreter means special casing in type checkers".
The concept of "hashable" and "unhashable" classes has been a bit of the sore point in the past. typeshed uses
__hash__: ClassVar[None]to mark classes as non-hashable, although that requires a# type: ignore[assignment]annotation. I think this is special-cased by at least some type checkers for marking classes as unhashable.Except for some wordage in the dataclasses section, the typing spec is silent about hashability. Since I believe that this needs special-casing by type checkers, it should be added to the typing spec. A raw idea:
object.__hash__()being implemented and typeshed having a type corresponding type annotation.)__hash__: ClassVar[None]. We could alternatively use something simpler such as__hash__ = None. I believe we used to use that, but I don't know why it's changed.def __hash__(self, ...) -> ..., usuallydef __hash__(self) -> int: ....