Встречается на 26% собеседований по Python

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

Ключом в словаре Python может быть любой хэшируемый объект, то есть объект, для которого функция hash() возвращает стабильное значение на протяжении всего времени существования объекта. На практике это означает, что ключами могут быть все неизменяемые типы данных: числа, строки, кортежи (если они не содержат изменяемых элементов), frozenset и другие. Изменяемые типы, такие как списки, множества или словари, использовать в качестве ключей нельзя, так как их хэш меняется при изменении содержимого, что нарушает целостность хеш-таблицы.

Как это работает

Словарь в Python — это хеш-таблица. При вставке пары ключ-значение Python вычисляет хэш ключа и по нему определяет место хранения. При поиске по ключу хэш вычисляется снова, что позволяет находить значение за O(1) в среднем. Для этого ключ должен быть хэшируемым: иметь метод __hash__ и поддерживать сравнение на равенство (__eq__). Неизменяемые объекты по умолчанию хэшируемы, потому что их состояние не меняется, и хэш остаётся постоянным.

Пример

python
# Допустимые ключи
valid_dict = {
    42: "число",
    "строка": "значение",
    (1, 2): "кортеж",
    frozenset({1, 2}): "frozenset"
}

# Недопустимый ключ: список (изменяемый)
try:
    {[1, 2]: "список"}
except TypeError as e:
    print(e)  # unhashable type: 'list'

# Кортеж с изменяемым элементом тоже не подойдёт
try:
    {(1, [2]): "кортеж со списком"}
except TypeError as e:
    print(e)  # unhashable type: 'list'

Подводные камни

  • Кортеж может быть ключом только если все его элементы хэшируемы. Например, (1, [2]) не подойдёт, так как список внутри не хэшируем.
  • Пользовательские классы по умолчанию хэшируемы (наследуют object.__hash__), но если вы переопределяете __eq__, то __hash__ становится None, и экземпляры перестают быть хэшируемыми. Чтобы сохранить хэшируемость, нужно явно задать __hash__.
  • Числа и строки разных типов могут иметь одинаковый хэш (коллизии), но это решается внутри словаря сравнением на равенство.

Когда использовать

Используйте неизменяемые типы как ключи для надёжности и предсказуемости. Если нужен ключ, который должен меняться, лучше пересоздавать словарь или использовать другие структуры данных. Для сложных составных ключей удобно использовать кортежи или frozenset.

Коротко

  • Ключом может быть любой хэшируемый объект, обычно это неизменяемые типы: int, str, tuple, frozenset.
  • Изменяемые типы (list, dict, set) не могут быть ключами.
  • Кортеж с изменяемыми элементами тоже не подходит.
  • При переопределении __eq__ в своём классе не забудьте задать __hash__, иначе экземпляры не будут хэшируемыми.
как отвечать на вопрос
пример собеседования
фреймворки на собеседовании
типичные вопросы junior
интервью вопросы и ответы