Вопрос с собеседования · Python

Как связаны __eq__ и __hash__ и что значит «хешируемый объект»?

MiddleООП
Ещё спрашивают так:
  • Почему список нельзя положить в set или сделать ключом словаря?
  • Что будет, если переопределить __eq__ без __hash__?
Коротко

Хешируемый объект имеет хеш, неизменный за всё время жизни, и сравнивается через __eq__. Правило: равные объекты обязаны иметь равные хеши. Если класс переопределяет __eq__ без __hash__, Python ставит __hash__ = None — экземпляры становятся нехешируемыми.

  • list, dict, set изменяемы и нехешируемы; tuple — хешируем, если хешируемы элементы
  • Хеш считают по тем же неизменяемым полям, что и равенство: hash((self.x, self.y))
  • @dataclass(frozen=True) генерирует согласованную пару

А ещё Хеш строк рандомизируется между запусками (PYTHONHASHSEED) — защита от атак на словари

Подробно

Как словарь ищет ключ: вычисляет hash(key), по нему находит ячейку, а при совпадении хешей сравнивает через ==. Если равные объекты дают разные хеши, словарь их не найдёт.

Почему изменяемые нехешируемы. Если бы список был ключом и потом изменился, его хеш изменился бы — запись осталась бы в ячейке старого хеша и стала недоступной.

class Point:
    def __init__(self, x, y): self.x, self.y = x, y
    def __eq__(self, other):
        return isinstance(other, Point) and (self.x, self.y) == (other.x, other.y)
    def __hash__(self):
        return hash((self.x, self.y))

По умолчанию объекты сравниваются по идентичности и хешируются по id — это согласовано. Переопределили __eq__ — Python обнулил __hash__, чтобы не допустить несогласованности; нужно определить его явно.

Возвращать NotImplemented из __eq__ для чужих типов — тогда Python попробует сравнение с другой стороны.

Что хотят услышать

  • Определение хешируемости и правило.
  • Поведение при переопределении только __eq__.
  • Почему изменяемые объекты нехешируемы.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✗Хеш стоит считать по изменяемым полям: так он отражает текущее состояние объекта и в словаре, и в set
  • ✓Равные объекты обязаны иметь равные хеши; __eq__ без __hash__ делает объект нехешируемым
  • ✗Равные объекты могут иметь разные хеши, а одинаковый хеш гарантирует, что объекты равны
  • ✗list хешируем, если все его элементы хешируемы, — точно так же, как кортеж из тех же элементов

Разбор темы

Связанные вопросы

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования