Какие типы данных в Python могут быть ключом в словаре (dict)?

«Какие типы данных в Python могут быть ключом в словаре (dict)?» — вопрос из категории Python, который задают на 26% собеседований Data Scientist / ML Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В 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, т.к. содержит список