Ответ
Чтобы объект класса можно было использовать в качестве ключа словаря (dict) или элемента множества (set), он должен быть хешируемым. Это означает, что у него должен быть постоянный хеш-код на протяжении всей его жизни.
Основные требования:
- Неизменяемость (Immutability): Все атрибуты, участвующие в вычислении хеша, должны быть неизменяемыми. Если хеш изменится после добавления объекта в словарь, объект станет недоступным.
- Реализация
__hash__(self): Метод должен возвращать целочисленное значение (хеш). - Реализация
__eq__(self, other): Метод для сравнения объектов. Еслиa == b, то обязательно должно бытьhash(a) == hash(b).
Современный подход (Python 3.7+): dataclasses
Самый простой и надежный способ — использовать декоратор @dataclass(frozen=True). Он автоматически генерирует __eq__ и __hash__ на основе атрибутов класса.
from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
x: int
y: int
p1 = Point(1, 2)
p2 = Point(1, 2)
points = {p1: "Начало"}
print(p1 == p2) # True
print(points[p2]) # Выведет: "Начало"
# p1.x = 3 # Вызовет FrozenInstanceError
Классический подход: ручная реализация
Если вы не можете использовать dataclasses, методы нужно реализовать вручную:
class PointManual:
def __init__(self, x, y):
self._x = x
self._y = y
def __eq__(self, other):
if not isinstance(other, PointManual):
return NotImplemented
return self.x == other.x and self.y == other.y
def __hash__(self):
# Хеш вычисляется на основе кортежа, т.к. кортежи неизменяемы
return hash((self._x, self._y))