mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 18:28:24 +00:00
Update docs for eq-without-hash
(#14885)
## Summary resolves #14883 This PR removes the known limitation section in the documentation of `eq-without-hash`. That is not actually a limitation as a subclass overriding the `__eq__` method would have its `__hash__` set to `None` implicitly. The user should explicitly inherit the `__hash__` method from the parent class. ## Test Plan <img width="619" alt="Screenshot 2024-12-20 at 2 02 47 PM" src="https://github.com/user-attachments/assets/552defcd-25e1-4153-9ab9-e5b9d5fbe8cc" /> --------- Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
This commit is contained in:
parent
51863b460b
commit
c0b0491703
1 changed files with 60 additions and 6 deletions
|
@ -11,14 +11,13 @@ 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`. 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.
|
||||
///
|
||||
/// ## Known problems
|
||||
/// Does not check for `__hash__` implementations in superclasses.
|
||||
/// 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.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```python
|
||||
/// class Person:
|
||||
/// def __init__(self):
|
||||
|
@ -29,6 +28,7 @@ use crate::checkers::ast::Checker;
|
|||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
///
|
||||
/// ```python
|
||||
/// class Person:
|
||||
/// def __init__(self):
|
||||
|
@ -40,6 +40,60 @@ use crate::checkers::ast::Checker;
|
|||
/// def __hash__(self):
|
||||
/// 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:
|
||||
///
|
||||
/// ```python
|
||||
/// 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__ = Person.__hash__
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: `object.__hash__`](https://docs.python.org/3/reference/datamodel.html#object.__hash__)
|
||||
/// - [Python glossary: hashable](https://docs.python.org/3/glossary.html#term-hashable)
|
||||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct EqWithoutHash;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue