Получить доступ

Как устроен словарь в Python?

JuniorКоллекции
Ещё спрашивают так:
  • Как работает dict внутри?
  • Почему поиск в словаре работает за O(1)?
  • Что может быть ключом словаря?
Коротко

dict — хеш-таблица: по hash(key) находится место ключа, поиск в среднем за O(1).

  • Ключ должен быть хешируемым и неизменным во время жизни
  • Порядок вставки сохраняется (гарантировано с Python 3.7)

А ещё Упомяните коллизии и почему в худшем случае поиск O(n)

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Подробно

При записи d[key] = value Python вычисляет hash(key) и по нему выбирает ячейку во внутренней таблице. При чтении происходит то же самое, и в ячейке сравниваются ключи: сначала хеши, затем ==. Поэтому поиск, вставка и удаление в среднем занимают O(1) и не зависят от размера словаря.

Коллизии. Разные ключи могут попасть в одну ячейку. CPython использует открытую адресацию: при занятой ячейке проверяет следующие по определённой последовательности. Когда таблица заполняется примерно на две трети, она увеличивается и элементы перераскладываются. В худшем случае, если хеши «плохие», поиск деградирует до O(n), но на практике это редкость.

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

Порядок. С Python 3.7 словарь гарантированно хранит ключи в порядке вставки: в CPython он устроен как компактный массив записей плюс отдельная таблица индексов.

Пример

d = {"a": 1}
d[(1, 2)] = "кортеж — можно"
d[[1, 2]] = "список"      # TypeError: unhashable type: 'list'

class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y
    def __eq__(self, other):
        return (self.x, self.y) == (other.x, other.y)
    def __hash__(self):
        return hash((self.x, self.y))   # согласован с __eq__

Что хотят услышать

  • Хеш-таблица и средняя сложность O(1).
  • Требование хешируемости и связь __hash__ с __eq__.
  • Понимание коллизий и худшего случая.
  • Сохранение порядка вставки с 3.7.

Частые ошибки

  • «Словарь не упорядочен» — устаревшее утверждение.
  • Переопределить __eq__ без __hash__: объект станет нехешируемым.
  • «Поиск всегда O(1)» без оговорки про средний случай.

Что спросят дальше

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем