[pylint] De-emphasize __hash__ = Parent.__hash__ (PLW1641) (#18613)

Summary
--

This PR updates the docs for PLW1641 to place less emphasis on the
example of inheriting a parent class's `__hash__` implementation by both
reducing the length of the example and warning that it may be unsound in
general, as @AlexWaygood pointed out on Notion.

Test plan
--

Existing tests

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
Brent Westbrook 2025-06-10 14:21:34 -04:00 committed by GitHub
parent 6051a118d1
commit 6cd0669475
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -16,10 +16,10 @@ use crate::checkers::ast::Checker;
///
/// ## Why is this bad?
/// A class that implements `__eq__` but not `__hash__` will have its hash
/// method implicitly set to `None`, regardless of if a super class defines
/// `__hash__`. This will cause the class to be unhashable, will in turn
/// cause issues when using the class as a key in a dictionary or a member
/// of a set.
/// method implicitly set to `None`, regardless of if a superclass defines
/// `__hash__`. This will cause the class to be unhashable, which will in turn
/// cause issues when using instances of the class as keys in a dictionary or
/// members of a set.
///
/// ## Example
///
@ -46,52 +46,16 @@ use crate::checkers::ast::Checker;
/// return hash(self.name)
/// ```
///
/// This issue is particularly tricky with inheritance. Even if a parent class correctly implements
/// both `__eq__` and `__hash__`, overriding `__eq__` in a child class without also implementing
/// `__hash__` will make the child class unhashable:
///
/// ```python
/// class Person:
/// def __init__(self):
/// self.name = "monty"
///
/// def __eq__(self, other):
/// return isinstance(other, Person) and other.name == self.name
///
/// def __hash__(self):
/// return hash(self.name)
///
///
/// class Developer(Person):
/// def __init__(self):
/// super().__init__()
/// self.language = "python"
///
/// def __eq__(self, other):
/// return (
/// super().__eq__(other)
/// and isinstance(other, Developer)
/// and self.language == other.language
/// )
///
///
/// hash(Developer()) # TypeError: unhashable type: 'Developer'
/// ```
///
/// One way to fix this is to retain the implementation of `__hash__` from the parent class:
/// In general, it is unsound to inherit a `__hash__` implementation from a parent class while
/// overriding the `__eq__` implementation because the two must be kept in sync. However, an easy
/// way to resolve this error in cases where it _is_ sound is to explicitly set `__hash__` to the
/// parent class's implementation:
///
/// ```python
/// class Developer(Person):
/// def __init__(self):
/// super().__init__()
/// self.language = "python"
/// def __init__(self): ...
///
/// def __eq__(self, other):
/// return (
/// super().__eq__(other)
/// and isinstance(other, Developer)
/// and self.language == other.language
/// )
/// def __eq__(self, other): ...
///
/// __hash__ = Person.__hash__
/// ```