Вопросы по рантайму Python на собеседовании
Вопросы про рантайм отделяют тех, кто отлаживал прод, от тех, кто читал документацию. Обычно всё начинается с подсчёта ссылок и заканчивается практикой: как вы искали утечку и чем профилировали медленный обработчик.
Из чего состоит тема
Так тема разложена в тренажёре: движок ведёт прогресс по каждой подтеме отдельно и возвращает те, где вы ошибаетесь.
- Типизация и mypy15
- Упаковка и зависимости14
- Модель памяти13
- CPython и сборка мусора12
- Профилирование и перф12
Разборы подтем
Конспект по каждой: что это, как отвечать вслух, на чём валятся, плюс вопросы для самопроверки.
- CPython и сборка мусора12 вопросов
- Упаковка и зависимости Python14 вопросов
- Профилирование Python-кода12 вопросов
- Модель памяти Python13 вопросов
- Типизация и mypy15 вопросов
Примеры вопросов с разбором
- Как CPython по умолчанию освобождает память объекта?A)Только периодическим сборщиком мусора, запускаемым по таймеруB)Подсчётом ссылок: при падении счётчика до нуля — сразуC)Вручную: программист сам вызывает free на ненужных объектахD)Не раньше завершения процесса — память отдаётся только на выходе
показать ответ и разбор
+B)Подсчётом ссылок: при падении счётчика до нуля — сразу// разбор: Основной механизм CPython — подсчёт ссылок: у каждого объекта счётчик, и как только он падает до нуля (вышли из области видимости, del, переприсваивание), объект уничтожается немедленно и детерминированно. Поверх работает отдельный сборщик циклов — на случай, когда refcount сам не справляется.
- Зачем нужно виртуальное окружение (venv)?A)Ускорять импорт пакетов за счёт предварительной их компиляцииB)Изолировать зависимости проекта от системных и других проектовC)Запускать код в отдельном контейнере с ограничением прав доступаD)Хранить исходники проекта отдельно от установленных библиотек
показать ответ и разбор
+B)Изолировать зависимости проекта от системных и других проектов// разбор: venv создаёт изолированное окружение с собственным набором установленных пакетов, отвязанным от системного Python и других проектов. Так у проекта А может быть Django 4, у Б — Django 5, без конфликтов. Это базовая гигиена: ставить зависимости не глобально, а в окружение проекта.
- С чего начинают оптимизацию медленного кода?A)Оптимизируют все циклы подряд, чтобы наверняка что-то помоглоB)С измерения профайлером, а не с догадок о узком местеC)Меняют интерпретатор на более быстрый ещё до всяких замеровD)Сразу переписывают самый сложный на вид участок на месте
показать ответ и разбор
+B)С измерения профайлером, а не с догадок о узком месте// разбор: Оптимизация без измерения — гадание: интуиция про «где медленно» чаще ошибается. Сперва профилируют (cProfile, py-spy), находят реальные горячие точки, и только их оптимизируют. Правило: сначала измерь, потом меняй, потом снова измерь. Иначе усложняешь код там, где это ничего не даёт.
- Что такое переменная в Python по отношению к объекту?A)Именованная ячейка памяти фиксированного размера под значениеB)Копия значения, независимая от других переменных программыC)Имя-ссылка на объект, а не сама ячейка со значениемD)Указатель на область стека, живущий строго до конца функции
показать ответ и разбор
+C)Имя-ссылка на объект, а не сама ячейка со значением// разбор: В Python имя — это ссылка (binding) на объект в куче, а не ячейка со значением.
b = aкопирует ссылку: оба имени указывают на один объект. Для неизменяемых (int, str) разница незаметна, а для изменяемых (list) — критична: правка через одно имя видна через другое. - Проверяет ли Python аннотации типов во время выполнения?A)Нет: аннотации — подсказки, рантайм их не навязываетB)Да, при передаче неверного типа сразу бросается TypeErrorC)Да, но только для аргументов функций, не для возвращаемых значенийD)Да, если включить специальный флаг интерпретатора при запуске
показать ответ и разбор
+A)Нет: аннотации — подсказки, рантайм их не навязывает// разбор: Аннотации типов в Python — необязательные подсказки: интерпретатор их хранит (в __annotations__), но не навязывает. Передать str туда, где объявлен int, можно — упадёт лишь позже на несовместимой операции, а то и вовсе нет. Проверку до запуска делает внешний тайпчекер (mypy, pyright), а не рантайм.
- Что подсчёт ссылок сам по себе освободить не может?A)Обычные локальные переменные после выхода из функцииB)Большие списки и словари — они живут до конца процессаC)Строки, потому что все они интернированы и неизменяемы навсегдаD)Объекты в циклической ссылке друг на друга
показать ответ и разбор
+D)Объекты в циклической ссылке друг на друга// разбор: Если два объекта ссылаются друг на друга (a.ref=b, b.ref=a), их счётчики ссылок не падают до нуля даже после удаления внешних имён — цикл «держит сам себя». Такой мусор refcount не соберёт; для него есть отдельный циклический сборщик (модуль gc), который находит и разрывает недостижимые циклы.
- Почему для воспроизводимой сборки версии зависимостей пинуют (фиксируют)?A)Пины нужны лишь для приватных пакетов, публичные и так стабильныB)Иначе свежая версия зависимости молча сломает сборкуC)Без пинов pip вообще откажется устанавливать зависимости проектаD)Пины ускоряют установку, потому что pip не ищет свежие версии
показать ответ и разбор
+B)Иначе свежая версия зависимости молча сломает сборку// разбор: Без фиксации версий pip ставит последнее совместимое, и на разных машинах/во времени набор зависимостей расходится — сборка «у меня работала», а в CI или на проде падает из-за нового релиза транзитивной зависимости. Пины (а лучше lock-файл) фиксируют точный набор и делают сборку воспроизводимой.
- Веб-эндпоинт тормозит. Что чаще оказывается узким местом в типичном бэкенде?A)Размер исходного кода модуля, который парсится на каждый запросB)Скорость самого интерпретатора Python на арифметике в хендлереC)Ожидание I/O: запросы к БД и внешним сервисам, а не Python-счётD)Количество комментариев и аннотаций типов в коде эндпоинта
показать ответ и разбор
+C)Ожидание I/O: запросы к БД и внешним сервисам, а не Python-счёт// разбор: В типичном бэкенде время уходит не на Python-арифметику, а на ожидание: запросы к БД (особенно N+1), вызовы внешних API, сериализация больших ответов. Поэтому и профилируют сначала эти границы. Микрооптимизация Python-циклов при медленных запросах к БД — оптимизация не того.
- Чем
isотличается от==?A)== сравнивает адреса в памяти, а is — содержимое объектовB)Это синонимы, разница только в скорости выполнения сравненияC)is для чисел, == для строк — по типу сравниваемых данныхD)is сравнивает идентичность объектов, == — их значенияпоказать ответ и разбор
+D)is сравнивает идентичность объектов, == — их значения// разбор: == вызывает __eq__ и сравнивает значения (два разных списка с одинаковым содержимым равны). is проверяет идентичность — это буквально один и тот же объект в памяти. Для None/True/False используют is (они синглтоны). Путаница даёт коварные баги на границе кэшируемых объектов.
это 9 из 66
Ещё 57 вопросов по теме — в тренажёре, с движком повторения
Прочитать разбор и ответить самому — разные навыки. В Сеньорчике вопросы идут сессиями, а движок возвращает подтемы, где вы ошибаетесь, пока они не начнут отскакивать. Бесплатно, лимит по энергии.
Частые вопросы
Как в Python устроена сборка мусора?
Основной механизм это подсчёт ссылок, а циклические ссылки убирает отдельный сборщик поколений. На собесе часто просят объяснить, почему объект может остаться в памяти.
Чем профилировать медленный код?
Начинают с измерения, а не с догадок: cProfile для функций, трассировка по времени для запросов, отдельные инструменты для памяти. Главное в ответе это порядок действий.