Ответ
В Python ключом словаря может быть любой хешируемый (hashable) объект. Хешируемость означает, что объект имеет неизменяемое значение хеша на протяжении всей его жизни и может быть сравнен с другими объектами (имеет методы __hash__() и __eq__()).
Основные хешируемые (допустимые) типы:
- Неизменяемые встроенные типы:
int,float,str,bytes,tuple,frozenset,bool. - Пользовательские классы: Экземпляры пользовательских классов по умолчанию хешируемы (хеш основан на
id()). Однако, если вы переопределите__eq__(), то должны также явно определить__hash__()для сохранения хешируемости.
Нехешируемые (недопустимые) типы:
- Изменяемые коллекции:
list,set,dict. - Массивы NumPy и другие изменяемые объекты.
Почему это важно? Словарь внутри реализован как хеш-таблица. При вставке пары ключ-значение вычисляется хеш ключа, который определяет "ячейку" для быстрого поиска. Если бы ключ мог измениться, его хеш тоже изменился бы, и найти связанное с ним значение стало бы невозможно.
Примеры:
# Допустимые ключи
valid_dict = {
42: 'integer',
3.14: 'float',
'hello': 'string',
(1, 2, 3): 'tuple of integers',
('a', ('b', 'c')): 'nested tuple',
frozenset([1, 2]): 'frozenset',
True: 'boolean (подтип int)',
MyImmutableClass(): 'пользовательский класс'
}
# Вызовет TypeError: unhashable type
invalid_dict = {
[1, 2]: 'list', # НЕЛЬЗЯ
{'a': 1}: 'dict', # НЕЛЬЗЯ
{1, 2}: 'set' # НЕЛЬЗЯ
}
# Кортеж может быть ключом, только если все его элементы хешируемы
good_tuple_key = (1, 'a', (2, 3)) # ОК
bad_tuple_key = (1, [2, 3]) # TypeError, т.к. содержит список