Собеседование Python-разработчика охватывает внутренности языка, асинхронность, Django или FastAPI и работу с базой. Ниже — самые частые вопросы с эталонными ответами. Middle: более глубокое понимание, оптимизацию и реальные рабочие ситуации.
1
В чём разница между списком и кортежем? Когда выбрать каждый?
Ответ
Списки изменяемы, кортежи неизменяемы, из-за чего кортежи хешируемы и годятся в качестве ключей словаря. Кортежи чуть быстрее и сигнализируют читателю, что это фиксированная запись, а не растущая последовательность. Список берут, когда содержимое меняется, кортеж — для фиксированных разнородных данных вроде координат или возвращаемой пары.
2
Что такое декоратор? Напишите декоратор, измеряющий время выполнения.
Ответ
Декоратор — это вызываемый объект, который принимает функцию и возвращает замену, чтобы добавить поведение, не трогая исходное тело. Декоратор замера времени оборачивает вызов: засечь perf_counter до, вызвать функцию, засечь после, залогировать разницу и вернуть результат. Обязательно применяйте functools.wraps к внутренней функции, чтобы имя, docstring и сигнатура оригинала пережили интроспекцию.
3
Объясните разницу между генератором и обычной функцией. Зачем нужен yield?
Ответ
Обычная функция вычисляет всё и возвращает один раз; генератор приостанавливается на каждом yield и возобновляется с того же места, отдавая значения лениво. Это значит постоянный расход памяти независимо от количества элементов, поэтому можно итерироваться по файлу в гигабайты или по бесконечной последовательности. Именно yield превращает функцию в конечный автомат, который интерпретатор умеет ставить на паузу.
4
Что такое *args и **kwargs? Приведите пример использования.
Ответ
*args собирает лишние позиционные аргументы в кортеж, **kwargs собирает лишние именованные в словарь. Их применяют, когда функция должна принимать произвольную сигнатуру — чаще всего в декораторах и обёртках, пробрасывающих всё дальше. Зеркальное применение — распаковка: вызов f(*items, **options) разворачивает последовательность и словарь в аргументы.
5
В чём разница между multiprocessing, threading и asyncio?
Ответ
Threading даёт конкурентность внутри одного процесса и помогает только на I/O из-за GIL. Multiprocessing поднимает отдельные интерпретаторы с настоящим параллелизмом ценой запуска процессов и межпроцессного обмена — это правильный выбор для CPU-нагрузки. asyncio это однопоточная кооперативная конкурентность: задачи уступают управление в точках await, что масштабируется на десятки тысяч сокетов, но блокирует всё, если внутри корутины вызвать синхронный код.
6
Что такое контекстный менеджер? Как реализовать свой?
Ответ
Контекстный менеджер гарантирует подготовку и завершение вокруг блока — именно это использует with, чтобы закрыть файл или отпустить блокировку даже при исключении. Реализовать можно классом с __enter__ и __exit__ либо генератором под декоратором @contextlib.contextmanager, который делает один yield между кодом подготовки и очистки. Метод __exit__ получает информацию об исключении и может подавить его, вернув True.
7
Объясните MRO и множественное наследование.
Ответ
Method Resolution Order задаёт порядок, в котором Python ищет атрибут по классам, и вычисляется алгоритмом линеаризации C3. Он гарантирует, что класс всегда идёт раньше своих родителей и что порядок базовых классов соблюдается, благодаря чему ромбовидное наследование ведёт себя предсказуемо. super() идёт по MRO, а не прыгает сразу к родителю, поэтому кооперативное множественное наследование требует, чтобы каждый класс в цепочке вызывал super().
8
Что такое N+1 в Django ORM? Как помогают select_related и prefetch_related?
Ответ
N+1 возникает, когда queryset возвращает N объектов, и каждое обращение к связанному полю порождает отдельный запрос. select_related решает это для ForeignKey и OneToOne, делая SQL JOIN и заполняя связанный объект тем же запросом. prefetch_related работает с ManyToMany и обратными связями: выполняет второй запрос и соединяет результаты уже в Python, поэтому запросов получается два, а не один.
9
В чём разница между shallow copy и deep copy?
Ответ
Поверхностная копия создаёт новый внешний контейнер, но сохраняет ссылки на те же вложенные объекты, поэтому изменение вложенного списка видно через обе копии. Глубокая копия рекурсивно дублирует всё и даёт полную независимость ценой времени и памяти. На практике из этого рождается баг с изменяемым значением по умолчанию или общим вложенным конфигом, который один вызывающий меняет для всех.
10
В чём разница между __str__ и __repr__?
Ответ
__repr__ адресован разработчику и в идеале должен быть однозначным — часто это выражение, которым объект можно было бы воссоздать; именно его показывают REPL и отладчики. __str__ адресован конечному пользователю, его вызывают print() и str(), а при отсутствии он падает обратно на __repr__. Привычка реализовывать __repr__ ценнее, потому что она делает читаемыми логи и трейсбеки.
🦎
Прочитать ответы недостаточно
На собеседовании ты говоришь вслух под давлением. Кем задаст эти же вопросы, оценит каждый ответ и покажет, что именно подтянуть.
Пройти интервью Middle Python Backend →Бесплатно · 3 интервью в месяц