Почему в Python ключом словаря может быть только хэшируемый объект

Ответ

Словари в Python (dict) реализованы на основе хеш-таблиц для обеспечения очень быстрого доступа к элементам (в среднем O(1)). Работа хеш-таблицы требует, чтобы ключ был хэшируемым.

Хэшируемый объект должен удовлетворять двум условиям:

  1. Иметь метод __hash__(), который возвращает одно и то же целое число (хеш) на протяжении всей жизни объекта.
  2. Иметь метод __eq__() для сравнения с другими объектами. Если a == b, то обязательно должно выполняться hash(a) == hash(b).

Изменяемые (mutable) объекты, такие как списки (list) или другие словари (dict), не могут быть ключами. Если бы значение такого объекта изменилось после добавления в словарь, его хеш также бы изменился, и объект стало бы невозможно найти.

Пример:

# Корректно: кортеж (tuple) является неизменяемым и хэшируемым
valid_key = (1, 2)
data = {valid_key: "value"}
print(data[(1, 2)])  # Вывод: value

# Ошибка: список (list) является изменяемым и нехэшируемым
invalid_key = [1, 2]
try:
    data = {invalid_key: "value"}
except TypeError as e:
    print(e)  # Вывод: unhashable type: 'list'

Ответ 18+ 🔞

А, слушай, вот тебе про словари в Питоне, это вообще отдельная песня, блядь. Они там внутри устроены на хеш-таблицах, чтобы всё летало как угорелое, в среднем за O(1). Но тут, сука, есть один важный нюанс, который всех новичков накрывает медным тазом!

Чтобы ключ в словарь воткнуть, он должен быть хэшируемым. А что это значит, спросишь ты? А значит вот что, пизда с ушами:

  1. У объекта должен быть метод __hash__(), который, как клятва верности, возвращает одно и то же число на протяжении всей его гребаной жизни.
  2. Должен быть метод __eq__() для сравнения. И главное правило, ёпта: если a == b, то и hash(a) обязан быть равен hash(b). Иначе пиздец и бардак.

И вот тут-то собака и зарыта! Объекты изменяемые, как твои планы на выходные, — списки (list) или другие словари — ключами быть не могут. Представь, взял ты список, засунул его как ключ, а потом взял и изменил его содержимое. Его хеш, сука, поменяется! И как его потом в этой хеш-таблице найти? Да никак, он там потеряется нахуй, как иголка в стоге сена после запоя.

Смотри, пример, чтобы совсем понятно стало:

# Всё чётко: кортеж (tuple) — неизменяемый и хэшируемый, как скала.
valid_key = (1, 2)
data = {valid_key: "value"}
print(data[(1, 2)])  # Выведет: value

# А вот тут будет пиздец и TypeError: список — изменяемый, нехэшируемый.
invalid_key = [1, 2]
try:
    data = {invalid_key: "value"}
except TypeError as e:
    print(e)  # Выведет: unhashable type: 'list'

Вот и весь сказ, блядь. Запомни раз и навсегда: в ключи — только неизменяемое барахло. А то будешь как Герасим, который Муму утопил, только тут ты сам себе программу утопишь, и совесть потом ебать будет.