Что нужно сделать с классом Value, чтобы объекты с одинаковым атрибутом v считались уникальными в множестве
Что проверяют
Знаете ли вы контракт хеширования: множество сначала считает хеш, выбирает корзину и только внутри неё сравнивает объекты через __eq__. Отсюда понятно, почему один метод без другого задачу не решает.
Как отвечать
Сперва уточнить постановку вслух: если нужно, чтобы два объекта с одинаковым v множество считало одним элементом, задают оба метода. Если наоборот, каждый экземпляр должен остаться отдельным, делать не надо ничего: по умолчанию сравнение и хеш идут по id объекта.
class Value:
def __init__(self, v):
self.v = v
def __eq__(self, other):
if not isinstance(other, Value):
return NotImplemented
return self.v == other.v
def __hash__(self):
return hash(self.v)Правила, которые стоит проговорить:
- равные объекты обязаны давать равный хеш, обратное не требуется: совпадение хешей это лишь общая корзина;
NotImplementedвместоFalseпозволяет Python попробовать сравнение с другой стороны;vдолжен быть хешируемым: список внутри дастTypeError, кортеж подойдёт;- значение
vнельзя менять, пока объект лежит в множестве.
То же самое короче даёт @dataclass(frozen=True): он генерирует __eq__ и __hash__ и запрещает присваивание полей.
Где спотыкаются
Определяют только __eq__. Python в этом случае выставляет __hash__ в None, и экземпляр становится нехешируемым, множество отвечает unhashable type. Зеркальная ошибка тоже встречается: один __hash__ без __eq__ оставляет сравнение по id, и оба объекта спокойно лежат в множестве, просто в одной корзине.
Меняют v у объекта, который уже лежит в множестве. Хеш считается заново по новому значению, а объект физически остался в старой корзине, поэтому проверка вхождения его не находит, а удаление даёт KeyError. Такие ключи делают неизменяемыми.
Берут @dataclass без frozen. Сгенерированный __eq__ обнуляет __hash__, и класс перестаёт годиться для множества. Нужен либо frozen=True, либо явный unsafe_hash=True, и в обоих случаях остаётся требование не менять поля после вставки.