Какой объект в Python считается хешируемым и каковы требования к нему

Ответ

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

Основные требования к хешируемому объекту:

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

Почему это важно? Словари и множества используют хеш для быстрого поиска. Сначала вычисляется хеш ключа, чтобы найти нужную "корзину" (bucket), а затем используется __eq__() для поиска конкретного элемента среди всех объектов в этой корзине (в случае коллизии).

Примеры:

  • Хешируемые типы (неизменяемые): int, float, str, tuple, frozenset.
  • Нехешируемые типы (изменяемые): list, dict, set.
# Хешируемый тип (tuple)
hashable_key = (1, 'a')
my_dict = {hashable_key: 'some_value'}
print(f"Словарь с tuple-ключом: {my_dict}")

# Попытка использовать нехешируемый тип (list)
unhashable_key = [1, 'a']
try:
    another_dict = {unhashable_key: 'another_value'}
except TypeError as e:
    print(f"Ошибка: {e}")

Вывод:

Словарь с tuple-ключом: {(1, 'a'): 'some_value'}
Ошибка: unhashable type: 'list'

Ответ 18+ 🔞

А, ну вот, опять про эти хешируемые объекты, блядь. Слушай, это ж основа основ, а народ как будто в рот мне чих-пых, не понимает, что к чему.

Короче, представь себе, что у тебя есть словарь — это такой шкаф с ящиками, ёпта. А ключ от ящика — это и есть твой хешируемый объект. Если ключ кривой, из говна и палок слеплен, то ты этот ящик никогда не откроешь, или хуже того, откроешь, а там не твоё барахло.

Так вот, чтобы ключ был годный, он должен соблюдать два правила, блядь, железных:

  1. У него должен быть метод __hash__(). Это как серийный номер на ключе, целое число. И главное, сука, чтобы этот номер не менялся, пока ключ живёт. Представь, ты пришёл к шкафу, номер на ключе 123, открыл ящик. Пришёл на следующий день, а на ключе уже 456. Ну и как, блядь, ты теперь свой ящик найдёшь? Пиздец.

  2. У него должен быть метод __eq__(). Это для сравнения, когда два ключа в одну корзину попадают (коллизия, блядь, называется). Если два ключа равны (a == b), то и хеши у них должны быть одинаковые, а то получится ебаный цирк: ключи одинаковые, а открывают разные ящики. Волнение ебать, терпения ноль.

Кто годный, а кто — мудя?

  • Годные (неизменяемые): int, str, tuple, frozenset. Как отлитые из стали, блядь. Раз создал — и хеш вечный.
  • Мудя (изменяемые): list, dict, set. Сегодня он один, завтра ты в него что-то добавил, хеш поменялся, и всё, пидары налетели, структура данных ебнулась.
# Вот смотри, кортеж — красава, хешируется
norm_key = (1, 'a')
my_cool_dict = {norm_key: 'значение'}
print(f"Всё ок: {my_cool_dict}")

# А теперь список, пидарас шерстяной
bad_key = [1, 'a']
try:
    fail_dict = {bad_key: 'пиздец'}
except TypeError as e:
    print(f"Ну вот, обосрались: {e}") # Справедливо скажет "unhashable type: 'list'"

Вывод-то какой будет? Правильный, блядь:

Всё ок: {(1, 'a'): 'значение'}
Ну вот, обосрались: unhashable type: 'list'

Вот и вся философия. Не хочешь проблем — делай ключи из того, что не ломается. А то будешь потом как Герасим, с мешком на озере стоять и думать: «Что же я, мудак, сделал?».