> Как устроены структуры данных list, dict и set в Python? (Python)
Уровень: senior · Роль: backend · Язык: Python · Категория: Технические вопросы
Компании: Wildberries, ИП Калюков Николай Сергеевич, Домклик
Стек: Python
> Пример ответа
Короткий ответ
list - динамический массив ссылок на объекты с амортизированной O(1) вставкой в конец и O(n) в середину. dict - хеш-таблица с открытой адресацией, хранит пары ключ-значение, средний доступ O(1). set - реализован как dict без значений, использует те же механизмы хеширования и разрешения коллизий. Все три структуры гетерогенны, хранят ссылки на объекты, а не сами объекты.
Подробное объяснение
list:
- Внутри - массив указателей (
PyObject**), размер которого больше числа элементов (capacity > size). - При переполнении - realloc с увеличением capacity примерно в 1.125 раза (формула
new_allocated = (size >> 4) + (size < 8 ? 3 : 6) + size). - Вставка/удаление в середине - сдвиг всех последующих элементов, O(n).
- Индексация - O(1), прямой доступ по смещению.
dict:
- Хеш-таблица с открытой адресацией (open addressing), схема probing - линейная с псевдослучайным шагом (perturb).
- Таблица хранит записи в двух массивах:
indices(индексы) иentries(пары ключ-значение + хеш). - Load factor ~2/3, при превышении - resize в 2 раза (или в 4 при маленьких размерах).
- Поиск: вычисление хеша → маскирование по размеру → проверка пустой ячейки → сравнение хешей и ключей (сначала по хешу, потом по
==). - Ключи должны быть hashable и immutable (на практике - неизменяемые типы).
- Порядок вставки сохраняется с Python 3.7 (implementation detail в 3.6).
set:
- Использует ту же структуру, что и
dict, ноentriesхранят только ключи (без значений). - Операции union/intersection/difference оптимизированы через хеш-таблицы.
- Для маленьких множеств (< 5 элементов) - отдельная оптимизация через линейный поиск.
Ключевые отличия от других языков:
- В отличие от Java
HashMap, Python dict не использует chaining (связные списки), а открытую адресацию - это даёт лучшую локальность кэша. - list - не linked list, поэтому операции в начале - O(n), в отличие от
deque.
На практике
- Для частых вставок/удалений в начало - использовать
collections.deque. - Для больших dict с числовыми ключами - рассмотреть
arrayилиnumpy. - Хеширование строк в Python рандомизировано (SipHash) - защита от DoS-атак через коллизии.
- Не использовать изменяемые объекты как ключи dict - нарушение инварианта хеш-таблицы.
- Для слияния dict - оператор
|(Python 3.9+) создаёт новый dict, аupdateмутирует существующий. set- идеален для проверки принадлежности, удаления дубликатов, но не для упорядоченных данных.
Пример кода
PYTHON# Внутреннее устройство list: амортизированный ростimport syslst = []for i in range(1000):lst.append(i)if i % 100 == 0:print(f"size={i}, allocated={sys.getsizeof(lst)} bytes")# dict: порядок вставки сохраняетсяd = {}for k in ["b", "a", "c"]:d[k] = 1print(list(d.keys())) # ['b', 'a', 'c']# set: хеширование и коллизииclass BadHash:def __hash__(self):return 42 # все объекты в одну корзинуdef __eq__(self, other):return self is others = {BadHash() for _ in range(100)} # O(n^2) из-за коллизий# Практический пример: дедупликация с сохранением порядкаdef dedupe(items):seen = set()for item in items:if item not in seen:yield itemseen.add(item)
Как отвечать на собеседовании
Начни с краткого определения каждой структуры, затем переходи к деталям реализации. Упомяни амортизационную сложность и trade-off между памятью и скоростью. Если спросят про коллизии - объясни схему probing и почему она лучше chaining для CPU-кэша. Для senior-уровня важно показать понимание внутреннего устройства: два массива в dict, формула роста list, влияние load factor на производительность. Свяжи ответ с практикой: когда выбирать list вместо set, почему нельзя использовать list как ключ dict.
Что проверяет интервьюер
- Понимание разницы между амортизированной и гарантированной сложностью.
- Знание внутреннего представления данных (массив vs linked list, хеш-таблица).
- Умение объяснить trade-off: память vs скорость, порядок vs скорость поиска.
- Понимание требований к ключам dict/set (hashable, immutable).
- Осведомлённость о деталях CPython (открытая адресация, SipHash, порядок вставки).
Типичные ошибки
- Утверждение, что
list- это linked list (путаница сcollections.deque). - Заявление, что
dict- это красно-чёрное дерево (как в C++std::map). - Игнорирование того, что
setиdictтребуют hashable ключи. - Утверждение, что вставка в
listв начало - O(1) (это O(n)). - Непонимание, что
dictв Python 3.7+ гарантирует порядок вставки (это не случайность). - Путаница между
==иisпри сравнении ключей в хеш-таблице.
> Похожие задачи по Python
Работал ли ты с Docker и Docker Compose
Какие стеки технологий вы использовали
Какие HTTP статусы существуют и каково назначение 2xx, 3xx, 4xx, 5xx
Как происходит передача аргументов в функцию в Python
> Похожие задачи по backend
Работал ли ты с Docker и Docker Compose
Какие стеки технологий вы использовали
Какие HTTP статусы существуют и каково назначение 2xx, 3xx, 4xx, 5xx
Как происходит передача аргументов в функцию в Python
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью