Для чего нужна хешируемость объектов в Python

Ответ

Хешируемость (hashability) — это свойство объекта, которое позволяет быстро находить его в коллекциях с доступом по ключу, таких как словари (dict) и множества (set).

Объект считается хешируемым, если он имеет хеш-значение, которое не меняется в течение его жизни. Это значение, вычисляемое методом __hash__(), используется для определения, где хранить объект в памяти (в какой "корзине" хеш-таблицы).

Зачем это нужно?

Хеширование позволяет выполнять операции поиска, вставки и удаления элементов в словарях и множествах в среднем за константное время — O(1) — что делает их чрезвычайно эффективными.

Правила хешируемости:

  1. Объект должен реализовывать методы __hash__() и __eq__().
  2. Главное правило: если a == b истинно, то hash(a) == hash(b) также должно быть истинно.
  3. Хеш-значение не должно изменяться. Поэтому хешируемыми могут быть только неизменяемые (immutable) объекты.

Примеры:

Встроенные неизменяемые типы (int, str, tuple, frozenset) являются хешируемыми по умолчанию:

# Корректное использование
my_dict = { "key": 1, 123: 2, (1, 2): 3 }
my_set = { "a", "b", "c" }

Изменяемые типы (list, dict, set) не являются хешируемыми, так как их содержимое (и, следовательно, хеш) может измениться. Попытка их использования вызовет ошибку TypeError:

# Этот код вызовет TypeError: unhashable type: 'list'
try:
    my_dict = { [1, 2]: "value" }
except TypeError as e:
    print(e)

Ответ 18+ 🔞

А, ну вот, слушай, объясняю про эту самую хешируемость, блядь. Это такая штука, которая позволяет объекту не просто так в словаре валяться, а чтобы его можно было найти за ебаное мгновение ока, как ключ в dict или элемент в set.

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

А нахуя это вообще нужно?

А затем, сука, чтобы поиск, добавление и удаление в словарях и множествах работали за константное время — O(1). То есть овердохуища быстро, а не как у тебя в голове соображалка.

Правила, которые надо соблюдать, а то будет пиздец:

  1. Объект должен уметь __hash__() и __eq__().
  2. Самое главное, блядь: если a == b — правда, то и hash(a) == hash(b) тоже должна быть правда. Иначе всё, пидары налетели, логика накрылась медным тазом.
  3. Хеш не должен меняться. Поэтому хешируемыми могут быть только неизменяемые (immutable) объекты, как святой грааль.

Примеры, чтобы ты понял, жопа с ручками:

Встроенные неизменяемые типы (int, str, tuple, frozenset) — они хешируемые по умолчанию, их можно спокойно использовать:

# Всё норм, всё работает
my_dict = { "key": 1, 123: 2, (1, 2): 3 }
my_set = { "a", "b", "c" }

А вот изменяемые типы (list, dict, set) — нихуя не хешируемые, потому что их содержимое (а значит и хеш) может поменяться. Попробуешь — получишь TypeError прямо в ебало:

# Этот код вызовет TypeError: unhashable type: 'list'
try:
    my_dict = { [1, 2]: "value" }
except TypeError as e:
    print(e)

Вот и вся магия, блядь. Хешируемость — это про скорость и неизменность. Запомни, и не позорься.