Sobes.tech
Назад к вопросам
Junior — Middle
45

Объясните, каким образом реализуется сравнение объектов через __eq__, если эти объекты используются в качестве ключей в словаре?

Компании, где спрашивали
OkkoOkko

Ответ от нейросети

sobes.tech AI

Что хотят услышать интервьюеры:

Нужно понимать, что словарь сначала ищет ключ по хешу, а при совпадении хеша уточняет совпадение через __eq__. Если __eq__ переопределён, то для ключей также важно, чтобы был согласован __hash__. Иначе объект может вести себя непредсказуемо: одинаковые по смыслу объекты будут считаться разными ключами или вообще станут неподходящими для словаря.

Определение:

В Python словарь хранит ключи в хеш-таблице. При вставке и поиске он сначала использует __hash__() для определения корзины, а затем __eq__() для проверки, считается ли найденный объект тем же ключом. Для корректной работы объектов в качестве ключей правило такое: если два объекта равны по __eq__, то их хеши должны быть одинаковыми. Если переопределить __eq__, но не обеспечить согласованный __hash__, объект может стать невалидным ключом или сравнение начнёт работать не так, как ожидается.

Пример использования:

class User:
    def __init__(self, user_id, name):
        self.user_id = user_id
        self.name = name

    def __eq__(self, other):
        if not isinstance(other, User):
            return NotImplemented
        return self.user_id == other.user_id

    def __hash__(self):
        return hash(self.user_id)


u1 = User(1, "Alice")
u2 = User(1, "Alicia")

d = {u1: "saved"}

print(d[u2])  # saved
print(u1 == u2)  # True

Пояснение кода:

В этом примере два разных объекта User считаются равными, потому что у них одинаковый user_id.
При создании словаря d = {u1: "saved"} Python вычисляет хеш u1 и кладёт запись в соответствующую корзину.
Когда запрашивается d[u2], Python сначала вычисляет хеш u2. Так как хеши совпадают, он переходит к проверке через __eq__ и видит, что u1 == u2. Поэтому значение находится успешно.

Если бы __hash__ не был переопределён согласованно с __eq__, словарь мог бы не найти нужный ключ даже при логическом равенстве объектов.

Ключевые моменты:

  • Словарь использует сначала __hash__, а потом __eq__.
  • __eq__ отвечает за логическое равенство ключей при совпадении хеша.
  • Для ключей в словаре важно, чтобы равные объекты имели одинаковый хеш.
  • Если переопределить __eq__, нужно обеспечить и корректный __hash__.
  • Изменяемые объекты в качестве ключей опасны, если их поля участвуют в хешировании или сравнении.
  • Если __eq__ возвращает NotImplemented, Python может попробовать обратное сравнение или считать объекты неравными.