Что может быть ключом в словаре?
В Python ключами в словаре (dict) могут быть только хешируемые (immutable) объекты.
Это значит, что ключ:
-
Должен быть неизменяемым (immutable),
-
Должен иметь определённый хеш (_hash_),
-
Должен поддерживать сравнение (_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\]) # Выдаст "точка"