Поведение dict при ключах без переопределённого __eq__

Словарь сохранит два разных ключа: одинаковый hash не делает экземпляры равными. Но хеш зависит от изменяемого списка, поэтому такая реализация небезопасна.

Короткий ответ

Что ответить на собеседовании

Словарь сохранит два разных ключа: одинаковый 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__. Для равенства по содержимому храните неизменяемый кортеж, сравнивайте его и вычисляйте хеш по этому же кортежу.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее