Ответ
За хэширование объектов в Python отвечают два dunder-метода: __hash__ и __eq__. Они должны быть реализованы вместе, чтобы объект мог корректно использоваться в хэшируемых коллекциях, таких как словари (dict) и множества (set).
__hash__(self): Должен возвращать целочисленное значение (хэш). Объекты с одинаковым содержимым, которые считаются равными, должны иметь одинаковый хэш.__eq__(self, other): Определяет логику сравнения двух объектов на равенство (==).
Золотое правило хэширования: если a == b истинно, то hash(a) == hash(b) также должно быть истинно.
Почему они нужны вместе?
Словари и множества используют хэш для быстрого поиска "корзины" (bucket), где может находиться элемент. Если в одной корзине оказывается несколько элементов (коллизия), Python использует __eq__ для поиска точного совпадения среди них.
Пример реализации
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
# Метод для сравнения объектов
def __eq__(self, other):
if not isinstance(other, Point):
return NotImplemented
return self.x == other.x and self.y == other.y
# Метод для вычисления хэша
# Хэшируем кортеж атрибутов, так как кортежи хэшируемы по умолчанию
def __hash__(self):
return hash((self.x, self.y))
# Теперь объекты класса Point можно использовать в множествах и как ключи словаря
p1 = Point(1, 2)
p2 = Point(1, 2) # Такой же объект
p3 = Point(3, 4)
points_set = {p1, p2, p3}
# В множестве останутся только уникальные объекты
# {<Point object at ...>, <Point object at ...>}
print(len(points_set)) # Вывод: 2
# p1 и p2 считаются равными
print(p1 == p2) # Вывод: True
# и имеют одинаковый хэш
print(hash(p1) == hash(p2)) # Вывод: True
Если определить только __eq__, класс по умолчанию становится нехэшируемым, и попытка добавить его экземпляр в set вызовет TypeError.