Что может быть ключом в словаре?

В Python ключами в словаре (dict) могут быть только хешируемые (immutable) объекты.
Это значит, что ключ:

  1. Должен быть неизменяемым (immutable),

  2. Должен иметь определённый хеш (_hash_),

  3. Должен поддерживать сравнение (_eq_).

🔑 Что такое словарь (dict)?

Словарь в Python — это структура данных, которая сопоставляет ключи и значения, как ячейки в таблице:

my_dict = {
"name": "Иван",
"age": 30
}

📌 Требования к ключам словаря

✅ Ключ должен быть:

  • Неизменяемым (immutable) — его содержимое не должно меняться.

  • Хешируемым — объект должен возвращать одинаковое значение при вызове hash() во время жизни.

  • Сравнимым — чтобы определить, равны ли два ключа.

🔷 Что такое хешируемость?

Объект хешируем, если:

  • Он реализует метод _hash_(), возвращающий целое число.

  • Он не изменяет свой хеш за время жизни.

📦 Это важно, потому что словарь работает как хеш-таблица.

✅ Примеры допустимых ключей (все хешируемые):

Тип ключа Пример Причина
Строка "ключ" Строки неизменяемы и хешируемы
--- --- ---
Число 42, 3.14 Числа — неизменяемые
--- --- ---
Кортеж (1, 2, 3) Только если все элементы хешируемы
--- --- ---
Логический тип True, False Булевы значения — это числа
--- --- ---
None None Разрешено
--- --- ---
Frozenset frozenset([1, 2]) Это неизменяемая версия множества
--- --- ---

❌ Примеры недопустимых ключей:

Объект Причина
Список ([1, 2]) Изменяемый → TypeError: unhashable
--- ---
Словарь ({'a': 1}) Изменяемый → TypeError
--- ---
Множество ({1, 2}) Изменяемое множество → TypeError
--- ---

🔍 Примеры:

▶ Допустимые ключи:

d = {
"name": "Анна",
123: "номер",
(1, 2): "точка",
True: "да",
None: "ничего"
}

▶ Ошибка:

d = {\[1, 2\]: "список"} # ❌ TypeError: unhashable type: 'list'

🧩 Ключи-кортежи: важное уточнение

Кортеж может быть ключом только если все его элементы тоже хешируемые:

t1 = (1, 2, 3) # ✅ Можно
t2 = (\[1, 2\], 3) # ❌ Нельзя (внутри список)
d = {}
d\[(1, 2)\] = "OK" # работает
d\[(\[1, 2\], 3)\] = "bad" # ошибка

🧠 Почему ключи должны быть хешируемыми?

Потому что Python использует алгоритм хеш-таблицы:

  • Вычисляется хеш ключа (hash()).

  • На основе хеша определяется, куда положить значение в памяти.

  • Для поиска и обновления значений используется сравнение (==).

Если объект можно изменить, то его хеш может измениться — и тогда Python потеряет доступ к этому ключу. Поэтому это запрещено.

🔎 Как проверить, хешируем ли объект?

print(hash("abc")) # OK
print(hash((1, 2))) # OK
print(hash(\[1, 2\])) # ❌ TypeError

Или использовать collections.abc.Hashable:

from collections.abc import Hashable
print(isinstance(3, Hashable)) # True
print(isinstance(\[1, 2\], Hashable)) # False
print(isinstance((1, 2), Hashable)) # True

📦 Как ведёт себя словарь при коллизиях?

Если у двух ключей одинаковый hash(), Python использует метод __eq__, чтобы проверить: действительно ли они равны, или просто совпал хеш.

🧠 Продвинутое: можно ли создать свой хешируемый класс?

Да! Нужно определить:

- \__hash_\_()  возвращает хеш-значение;      
- \__eq_\_(self, other)  для сравнения. 

▶ Пример:

class Point:
def \__init_\_(self, x, y):
self.x = x
self.y = y
def \__hash_\_(self):
return hash((self.x, self.y)) # создаём кортеж
def \__eq_\_(self, other):
return self.x == other.x and self.y == other.y
p1 = Point(1, 2)
p2 = Point(1, 2)
d = {p1: "точка"}
print(d\[p2\]) # Выдаст "точка"