Skip to content

eq-without-hash (PLW1641)

Derived from the Pylint linter.

This rule is unstable and in preview. The --preview flag is required for use.

What it does

Checks for classes that implement __eq__ but not __hash__.

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.

Example

class Person:
    def __init__(self):
        self.name = "monty"

    def __eq__(self, other):
        return isinstance(other, Person) and other.name == self.name

Use instead:

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)

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:

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:

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