You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
Classes are hashable by default (due to object.__hash__() being implemented and typeshed having a type corresponding type annotation.)
To mark a class as non-hashable, use __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.
To mark a class as hashable, use def __hash__(self, ...) -> ..., usually def __hash__(self) -> int: ....
Sub-classes inherit their parent's hashability, unless overridden.
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 that object is 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 that object has __hash__ of type None | () -> int, with a special case to allow instances of exactly object to 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.
This 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".
Activity
JelleZijlstra commentedon 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.srittau commentedon May 12, 2026
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.
randolf-scholz commentedon May 12, 2026
This doesn't match runtime, as cpython removes
__hash__if a subclass implements__eq__, but not__hash__:This is exactly what makes static typing for
__hash__so tricky..srittau commentedon May 13, 2026
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".