Вопрос с собеседования · Python

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

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

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

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

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

Подробно

При записи 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)» без оговорки про средний случай.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✗Отсортированное дерево ключей: поиск за O(log n), порядок вставки теряется
  • ✗Ключом может быть любой объект, включая список
  • ✗Ключи хранятся в списке, поиск идёт перебором за O(n)
  • ✓Хеш-таблица: место ключа ищется по hash(key), поиск в среднем O(1), ключ хешируемый

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

Разбор темы

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

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования