Словарь сохранит два разных ключа: одинаковый hash не делает экземпляры равными. Но хеш зависит от изменяемого списка, поэтому такая реализация небезопасна.
Поведение dict при ключах без переопределённого __eq__
Словарь сохранит два разных ключа: одинаковый hash не делает экземпляры равными. Но хеш зависит от изменяемого списка, поэтому такая реализация небезопасна.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Как будет работать словарь, если в качестве ключей использовать экземпляры пользовательского класса HashList, реализующего только метод __hash__, а метод __eq__ оставлен без переопределения?
from typing import Any
class HashList:
def __init__(self, data: list[int]) -> None:
self._data = data
def __hash__(self) -> int:
return hash(sum(self._data))
l1 = HashList([1, 2, 3])
l2 = HashList([5, 1, 0])
d1 = {
l1: "l1",
l2: "l2",
}
print(d1)
Ответ
Словарь сохранит два разных ключа: одинаковый hash не делает экземпляры равными. Но хеш зависит от изменяемого списка, поэтому такая реализация небезопасна.
У l1 и l2 сумма элементов равна 6, следовательно их хеши совпадут. Унаследованное от object равенство считает разные экземпляры различными, поэтому коллизия разрешается и обе записи остаются в d1.
Вывод содержит два стандартных представления объектов и значения 'l1', 'l2'; конкретные адреса заранее неизвестны. Если изменить _data, хеш объекта может поменяться, и поиск уже вставленного ключа станет ненадёжным.
Для идентичностной семантики оставьте стандартный object.__hash__. Для равенства по содержимому храните неизменяемый кортеж, сравнивайте его и вычисляйте хеш по этому же кортежу.