Python-секция на собеседовании редко проверяет, помните ли вы синтаксис, — его подскажет любой редактор. Проверяют другое: понимаете ли вы, что происходит под капотом, и умеете ли выбирать инструмент под задачу. Ниже — реальные вопросы по темам с краткими ориентирами, что именно хочет услышать интервьюер. Не заучивайте формулировки: важно проговаривать своими словами и с примерами из практики.
Как устроена Python-секция
Технические интервью на middle/senior обычно складываются из трёх частей. Теория — вопросы на понимание языка и CPython: GIL, модель памяти, дескрипторы. Лайвкодинг — задача на структуры данных или небольшой практический кейс (распарсить, агрегировать, написать декоратор), где смотрят не только на результат, но и на то, как вы рассуждаете. Вопросы по опыту и фреймворкам — как вы строили API на FastAPI, почему выбрали именно такую схему в SQLAlchemy, как ловили утечку памяти в проде.
Чем выше грейд, тем меньше «расскажите определение» и больше «почему так сделано» и «какой trade-off вы приняли». Разберём типичные блоки.
GIL и конкурентность
- Что такое GIL и зачем он нужен? Global Interpreter Lock — мьютекс, позволяющий выполнять байткод только одному потоку за раз в рамках процесса. Нужен для упрощения управления памятью (потокобезопасный рефкаунтинг) и совместимости C-расширений.
- threading vs multiprocessing vs asyncio — когда что?
threadingхорош для I/O-bound задач (GIL отпускается на I/O),multiprocessing— для CPU-bound (обходит GIL через отдельные процессы),asyncio— для большого числа I/O-операций в одном потоке без накладных расходов на потоки. - Что изменилось с free-threaded CPython? В Python 3.13 появилась экспериментальная сборка без GIL (PEP 703, флаг
--disable-gil). Это отдельный билд, не дефолтный; в 3.13 он официально experimental. Ждут понимания, что no-GIL — про настоящий параллелизм потоков на CPU, но пока с оговорками по стабильности и производительности однопоточного кода. - Отпускается ли GIL при сетевом запросе? Да — на блокирующих системных вызовах (I/O,
time.sleep) поток освобождает GIL, поэтому потоки реально полезны для I/O-bound нагрузки. - Почему
multiprocessingдороже потоков? Отдельные процессы не делят память, данные передаются через сериализацию (pickle), есть стоимость на старт процесса и IPC.
Генераторы и итераторы
- Чем итератор отличается от итерируемого? Iterable реализует
__iter__, итератор дополнительно__next__и хранит состояние перебора.forпод капотом вызываетiter(), затемnext()доStopIteration. - Как работает
yield? Превращает функцию в генератор: вызов возвращает объект-генератор, тело выполняется лениво, состояние замораживается междуnext(). Ждут слова «ленивость» и «состояние сохраняется». - Generator vs list comprehension — когда что? Генератор не материализует все элементы, экономит память на больших или бесконечных потоках; список — когда нужен повторный проход или индексация.
- Что делают
yield fromи.send()?yield fromделегирует итерацию вложенному генератору,.send(value)передаёт значение обратно в точкуyield— основа корутин до asyncio. - Можно ли пройти генератор дважды? Нет, он одноразовый — после исчерпания снова отдаёт
StopIteration. Частая ловушка в коде.
Декораторы и замыкания
- Что такое замыкание? Функция, захватывающая переменные из охватывающей области; они живут, пока жива вложенная функция. Изменение внешней переменной — через
nonlocal. - Как устроен декоратор? Функция, принимающая функцию и возвращающая обёртку.
@decorator— синтаксический сахар дляf = decorator(f). - Зачем
functools.wraps? Сохраняет метаданные оборачиваемой функции (__name__,__doc__, сигнатуру), иначе они подменяются обёрткой — ломает интроспекцию и документацию. - Как написать декоратор с аргументами? Дополнительный уровень вложенности: фабрика принимает аргументы и возвращает сам декоратор.
- Чем
lru_cacheпомогает и где опасен? Кэширует результаты по аргументам; опасен на изменяемых данных и потенциальной утечкой памяти приmaxsize=Noneна горячем пути.
ООП: MRO, dunder-методы, модели данных
- Как работает MRO? Method Resolution Order вычисляется алгоритмом C3-линеаризации; порядок виден в
Cls.__mro__. Решает diamond-проблему при множественном наследовании. - Зачем
super()и как он использует MRO?super()идёт по цепочке MRO, а не «к родителю», — поэтому в кооперативном наследовании важно вызыватьsuper().__init__()везде. - Какие dunder-методы стоит знать?
__init__/__new__,__eq__/__hash__(вместе),__repr__/__str__,__enter__/__exit__для контекст-менеджеров,__getitem__/__iter__для контейнеров. - dataclass vs attrs vs pydantic — в чём разница?
dataclass— встроенный, генерирует boilerplate, без валидации.attrs— мощнее и быстрее, с валидаторами и конвертерами.pydantic— валидация и парсинг данных по типам в рантайме, основа FastAPI. Ждут «pydantic валидирует, dataclass — нет». - Что делает
__slots__? Запрещает динамический__dict__, фиксирует набор атрибутов — экономит память на множестве объектов и чуть ускоряет доступ.
Типизация
- Что дают type hints в рантайме? По умолчанию ничего — это аннотации для статических анализаторов (mypy, pyright) и IDE; CPython их не проверяет. Ждут понимания, что типы не enforced без сторонних инструментов.
- Что такое
Protocol? Структурная типизация (duck typing на уровне типов): класс соответствует протоколу, если реализует нужные методы, без явного наследования. Optional[X]vsX | None? Эквивалентны; синтаксисX | Noneдоступен с Python 3.10 и считается предпочтительным.- Зачем
TypeVar/дженерики и что нового в 3.12? Для типобезопасных контейнеров и функций. В Python 3.12 появился новый синтаксис дженериков (PEP 695):def first[T](x: list[T]) -> Tбез явногоTypeVar. - mypy strict — что ловит? Запрещает неявный
Any, требует аннотаций, ловит несоответствия — ждут, что вы хоть раз гоняли строгий режим в CI.
Память и производительность
- Как устроено управление памятью? Основной механизм — подсчёт ссылок (reference counting): объект удаляется, когда счётчик ссылок падает до нуля. Поверх — циклический сборщик мусора, который ловит ссылочные циклы, недоступные рефкаунтингу.
- Зачем нужен циклический GC, если есть рефкаунтинг? Рефкаунтинг не разрывает циклы (
a.b = b; b.a = a) — такие объекты собирает поколенческий GC (gcмодуль). - Как искать утечки памяти?
tracemalloc,objgraph, ростgc.get_objects(); типичные причины — глобальные кэши, незакрытые ссылки, замыкания. - Что даёт
__slots__для памяти? Убирает per-instance__dict__— заметная экономия при миллионах мелких объектов. - Почему «преждевременная оптимизация — зло» здесь уместна? Ждут, что вы сначала профилируете (
cProfile,py-spy), а не угадываете узкое место.
asyncio
- Что такое event loop? Цикл, который выполняет корутины и колбэки в одном потоке, переключаясь на точках
await, когда операция ждёт I/O. Параллелизма по CPU не даёт — это конкурентность, не параллелизм. - Чем корутина отличается от Task? Корутина — объект из
async def, ничего не делает, пока её не запустят.Taskоборачивает корутину и планирует её выполнение в loop (asyncio.create_task). - Что значит
await? Уступает управление event loop до готовности awaitable; забытьawait— частая ошибка, корутина просто не выполнится. - Типичные гочи asyncio? Блокирующий вызов (
requests, тяжёлый CPU,time.sleep) внутри корутины замораживает весь loop; для блокирующего кода —run_in_executor. Ещё — смешивание sync и async библиотек. asyncio.gathervsTaskGroup?gatherзапускает корутины конкурентно и собирает результаты;TaskGroup(Python 3.11) даёт структурированную конкурентность с корректной отменой при ошибке.
Вопросы по экосистеме
Без глубокого погружения, но проверяют осознанность выбора:
- FastAPI vs Django — когда что? FastAPI — async-first, лёгкий, на pydantic, для API и микросервисов. Django — батарейки в комплекте (ORM, админка, auth), для монолитов с богатой доменной логикой.
- Зачем FastAPI зависит от pydantic? Валидация и сериализация request/response по type hints — автоматически, с генерацией OpenAPI-схемы.
- SQLAlchemy: что такое сессия и identity map? Сессия — unit of work, отслеживает изменения; identity map гарантирует один объект на строку в рамках сессии. Ждут понимания lazy vs eager loading и проблемы N+1.
- Как избежать N+1 в SQLAlchemy? Eager loading через
selectinload/joinedloadвместо ленивой подгрузки в цикле.
Чего ждут от middle vs senior
Middle должен уверенно отвечать «как это работает» и «когда использовать»: объяснить GIL, написать декоратор, выбрать между списком и генератором, не запутаться в asyncio на базовом уровне. Достаточно знания механизмов и здравого выбора инструментов.
Senior отвечает на «почему так в CPython» и «какой trade-off вы выбрали в проде». Здесь ждут историй: как вы диагностировали утечку памяти, почему отказались от потоков в пользу процессов, как структурировали async-сервис под нагрузку, какие грабли поймали. Senior умеет сказать «зависит от» и обосновать — а не выдать заученное определение.
Как готовиться
Сначала прогоните темы списком: по каждому блоку выше попробуйте вслух объяснить вопрос так, будто перед вами интервьюер. Места, где «вроде знаю, но сформулировать не могу», — ваши слабые точки. Параллельно тренируйте лайвкодинг на структурах данных и небольших практических задачах, проговаривая ход мысли.
Хорошо работает связка с LLM: попросите модель сыграть интервьюера и гонять вас по темам — подробный разбор такого подхода есть в гайде про подготовку к собеседованию с LLM. Но AI хвалит охотнее, чем стоит, и не передаёт давление живого диалога. Поэтому за несколько дней до реального интервью возьмите мок-собеседование с практикующим senior-инженером — он подсветит дыры, которые вы сами не замечаете, и потренирует отвечать под напряжением.
В IT-ХОЗЯЕВА мок-собеседования и доступ к базе практикующих менторов входят в тариф ХОЗЯИН за 1000 ₽/мес — можно прогнать Python-секцию с инженером по вашему стеку, получить разбор резюме и наметить план подготовки. Это удобный способ соединить самостоятельную теорию с живой обратной связью, не собирая контакты менторов по знакомым.
Главное — не заучивать ответы. Интервьюер слышит шаблон за секунду. Понимайте механизмы, держите наготове примеры из своей практики, и тогда даже неожиданный вопрос превратится в разговор, а не в провал.