Вопросы на собеседовании: Python-разработчик
100 реальных вопросов с образцовыми ответами и пояснениями для уровня Middle.
Смотреть пример резюме: Python-разработчик →Тренировка флешкарточками
Интервальное повторение · Hunter Pass
Вопросы
functools.wraps сохраняет идентичность декорируемой функции и открывает оригинал для интроспекции.
- Без него __name__ и __doc__ принадлежат врапперу, поэтому трейсбеки, логи, инструменты документации и отладка показывают не ту функцию.
- Библиотеки, завязанные на метаданные функций, могут работать неверно, включая пиклинг, functools.singledispatch и регистрацию роутов во фреймворках.
- wraps копирует __name__, __doc__, __module__ и __dict__, а затем задаёт __wrapped__, чтобы inspect.unwrap мог добраться до оригинальной функции.
Зачем это спрашивают: Интервьюер проверяет, отлаживал ли кандидат последствия потерянных метаданных, а не пишет wraps как карго-культ.
Декоратор с аргументами использует вложенные замыкания, чтобы сохранить настройки до момента вызова.
- Внешняя фабрика получает аргументы вроде times=3 и возвращает настоящий декоратор.
- Декоратор получает функцию и возвращает враппер, которому через замыкания доступны и функция, и настройки.
- Скобки обязательны даже при дефолтах, если не поддерживать обе формы через проверку первого позиционного аргумента на callable; более простой вариант состоит в том, чтобы требовать скобки и документировать это.
Зачем это спрашивают: Сильный ответ объясняет цепочку замыканий и зачем нужен лишний уровень вызова, а не воспроизводит заученную вложенность.
Сложенные декораторы применяются от функции наружу, а при вызове начинают выполняться в обратном порядке.
- Применение идёт снизу вверх: ближайший к def декоратор первым оборачивает функцию, а каждый следующий оборачивает предыдущий результат.
- Выполнение идёт сверху вниз: первым срабатывает самый внешний враппер и делегирует вызов внутрь.
- Порядок влияет на поведение: кеш снаружи проверки авторизации может кешировать ответы для неавторизованных пользователей, а обратный порядок пропускает каждый кешированный результат через авторизацию.
Зачем это спрашивают: Пример с авторизацией и кешем показывает, что кандидат понимает порядок как реальную проблему корректности, а не тривию.
__exit__ всегда получает возможность выполнить очистку и решает, выйдет ли исключение за пределы блока with.
- Он получает тип исключения, значение и трейсбек либо три значения None, если блок завершился нормально.
- Истинное возвращаемое значение подавляет исключение и продолжает выполнение после блока with, как это делает contextlib.suppress.
- Возврат None или False пропускает исключение дальше, но очистка в __exit__ в любом случае выполняется.
Зачем это спрашивают: Деталь о том, что возвращаемое значение управляет подавлением, отделяет тех, кто реализовывал контекст-менеджеры, от тех, кто их только использует.
contextlib.contextmanager сопоставляет две стороны единственного yield со входом в контекст и выходом из него.
- Код до yield работает как __enter__, а отданное значение становится целью после as.
- Код после yield работает как __exit__, поэтому yield нужно оборачивать в try/finally, чтобы гарантировать очистку при исключении в теле with.
- Исключение вбрасывается в генератор в точке yield, а перехват без повторного raise подавляет его так же, как возврат True из __exit__.
Зачем это спрашивают: Требование try/finally и есть классический баг, на который охотится вопрос, а сильные кандидаты называют механизм вбрасывания в генератор.
Генератор приостанавливает и возобновляет один сохранённый фрейм выполнения, поэтому данные можно обрабатывать по одному элементу.
- Вызов генераторной функции не выполняет её тело, а возвращает объект-генератор с приостановленным фреймом.
- Каждый next() продолжает выполнение до yield, отдаёт значение и снова останавливается, сохраняя локальные переменные и указатель инструкции.
- Пайплайн вроде parse(filter(read_lines(f))) проводит каждый элемент через все стадии и сохраняет постоянное потребление памяти даже для многогигабайтного лога.
Зачем это спрашивают: Интервьюер слушает ментальную модель приостановленного фрейма и конкретный стриминговый кейс, а не формулу yield возвращает несколько значений.
Список подходит для повторно используемых материализованных данных, а генератор для ленивого однопроходного потребления.
- Списковое включение сразу строит весь результат, поэтому подходит для проверки длины, нескольких проходов, индексации и сортировки.
- Генераторное выражение отдаёт значения лениво и хорошо сочетается с sum или any, где короткое замыкание позволяет избежать лишней работы.
- Генератор не подходит, если потребитель проходит по данным дважды: второй проход молча не вернёт элементов и не вызовет ошибку.
Зачем это спрашивают: Называние бага с тихим исчерпанием показывает, что кандидат на нём обжигался, а именно такую практическую глубину и прощупывают.
yield from делегирует полный протокол генератора, а не только передаёт выдаваемые значения.
- При простой итерации он похож на цикл for с yield, но send() и throw() от потребителя передаются в субгенератор.
- Возвращаемое значение субгенератора становится значением выражения yield from, что позволяет генератору давать и поток, и финальный результат.
- На этой передаче возвращаемых значений строились корутинные фреймворки до async, тогда как ручной цикл ломает двустороннюю связь.
Зачем это спрашивают: Упоминание делегирования send и захвата возвращаемого значения отличает настоящее понимание от сахара над циклом.
Лямбды в цикле захватывают одну общую переменную, поэтому при позднем вызове видят её последнее значение.
- Замыкания захватывают переменные, а не снимки их значений, и после цикла общая переменная содержит последний элемент.
- Аргумент по умолчанию вроде lambda x=x: ... привязывает текущее значение в момент определения; вместо этого functools.partial может сохранить его явно.
- Без такой привязки колбеки или словари обработчиков, созданные в цикле, заставляют каждый обработчик работать с последним элементом.
Зачем это спрашивают: Интервьюер хочет услышать позднее связывание как причину и идиому с аргументом по умолчанию как фикс, в идеале с контекстом регистрации колбеков.
Аргументы по умолчанию вычисляются один раз при определении функции, что опасно для изменяемых объектов, но полезно для намеренных снимков значений.
- def f(items=[]) повторно использует один список во всех вызовах, поэтому добавленные значения протекают между ними.
- Стандартное исправление использует сентинел None и выражение items = [] if items is None else items внутри функции.
- Привязка при определении может намеренно сохранить значение переменной цикла или закешировать дорогой поиск, хотя для кеширования лучше использовать явные механизмы.
Зачем это спрашивают: Знание механизма работает в обе стороны, и сильный кандидат может назвать легитимное применение вычисления при def, а не только баг.
Дескрипторы централизуют управление атрибутами через методы типа классового атрибута.
- Дескриптор определяет __get__, __set__ или __delete__, а доступ к атрибуту экземпляра направляется через эти методы посредством класса.
- property является встроенным дескриптором, который связывает чтение и запись с функциями; функции тоже являются дескрипторами, чей __get__ создаёт связанные методы.
- Собственный дескриптор полезен, когда логика вроде валидации полей или преобразования единиц повторяется во многих атрибутах и иначе потребовала бы множества похожих property.
Зачем это спрашивают: Связывание property и связанных методов с одним протоколом показывает системное понимание, а критерий переиспользования показывает практическое суждение.
__slots__ меняет динамические атрибуты экземпляра на фиксированное хранилище и снижает расход памяти на каждый объект.
- Он заменяет __dict__ каждого экземпляра хранилищем перечисленных атрибутов, заметно сокращая память и немного ускоряя доступ к атрибутам.
- Произвольные атрибуты в рантайме недоступны, слабые ссылки требуют явного слота __weakref__, а родитель без слотов возвращает __dict__ при наследовании.
- Это оправдано для миллионов экземпляров вроде узлов AST или точек тиковых данных, но обычным доменным классам чаще важнее гибкость.
Зачем это спрашивают: Критерий окупаемости через массовые количества экземпляров отделяет осознанное применение от преждевременной оптимизации.
dataclass генерирует типичные методы объекта данных из аннотированных полей и даёт безопасные средства для неизменяемости и дефолтов.
- Он создаёт __init__, __repr__ и __eq__, а при необходимости __hash__ и методы сравнения, убирая повторяющиеся конструкторы из одних присваиваний.
- frozen=True запрещает присваивание атрибутов, что позволяет хешировать экземпляры и безопасно использовать их как ключи словаря.
- field(default_factory=list) вызывает фабрику для каждого экземпляра и избегает бага с общим изменяемым дефолтом в виде обычного списка.
Зачем это спрашивают: Привязка default_factory к ловушке изменяемых дефолтов показывает, что кандидат понимает, почему API устроен именно так.
== сравнивает значения через __eq__, а is проверяет, указывают ли две ссылки на один объект.
- Равные строки, построенные в рантайме, могут быть разными объектами, хотя == вернёт True.
- CPython интернирует маленькие целые числа и некоторые строки времени компиляции, но поведение меняется для склеенных или пользовательских строк и между реализациями.
- Используйте is для синглтонов вроде None, True, False и собственных сентинел-объектов, но не для сравнения значений с опорой на интернирование.
Зачем это спрашивают: Ограничение is семантикой идентичности и синглтонами с объяснением ненадёжности проверок интернирования и есть ожидаемый зрелый ответ.
Поверхностное копирование дублирует только контейнер, а глубокое рекурсивно копирует его граф объектов.
- copy.copy создаёт новый внешний объект, но его элементы остаются прежними ссылками, поэтому изменение вложенного списка через копию меняет и оригинал.
- copy.deepcopy рекурсивно копирует граф и использует словарь memo для обработки циклов.
- Поверхностная копия конфига может распространить вложенное изменение одного тенанта на всех, но глубокое копирование огромных структур в горячих путях бывает дороже переработки кода без мутаций.
Зачем это спрашивают: Интервьюеру нужны механизм разделяемых ссылок плюс понимание реальной цены deepcopy, а не голые определения.
CPython нужен сборщик циклов, потому что один подсчёт ссылок не освобождает недостижимые объекты, которые ссылаются друг на друга.
- Подсчёт ссылок освобождает объект при нулевом счётчике, но участники цикла удерживают счётчики друг друга выше нуля.
- Поколенческий циклический сборщик периодически находит такие недостижимые группы; __del__ мешает этому из-за неоднозначного порядка финализации умирающего цикла.
- Исторически такие циклы не собирались, а сегодня финализаторы могут сработать в неожиданном порядке, поэтому ресурсы лучше очищать через контекст-менеджеры или weakref.finalize.
Зачем это спрашивают: Пример с циклом и предпочтение контекст-менеджеров вместо __del__ показывают операциональное понимание управления памятью.
GIL защищает внутреннее состояние интерпретатора CPython, но не обеспечивает потокобезопасность операций приложения.
- Он позволяет только одному потоку выполнять байткод Python в каждый момент и защищает от повреждения состояние интерпретатора, например счётчики ссылок.
- Действия из нескольких байткодов, такие как проверка с последующим обновлением или += для общего атрибута, всё равно могут перемежаться и вызывать гонки и потерю обновлений.
- GIL освобождается на блокирующем I/O и внутри многих C-расширений, поэтому потоки могут ускорять I/O-bound нагрузки.
Зачем это спрашивают: Различие между безопасностью интерпретатора и потокобезопасностью прикладного кода и есть ключевое заблуждение, которое вопрос призван вскрыть.
Потоки совмещают блокирующие ожидания в I/O-bound работе, но выполнение CPU-bound кода Python сериализуется из-за GIL.
- Поток освобождает GIL при блокирующем чтении сокета или обращении к базе, позволяя другим потокам работать и повышая пропускную способность при одновременных ожиданиях.
- CPU-bound потоки не ждут, а конкурируют за один GIL, поэтому работа сериализуется и может замедлиться из-за расходов на переключение.
- Для тяжёлых вычислений подходят multiprocessing, C-расширения вроде numpy, которые освобождают GIL внутри, или другой рантайм.
Зачем это спрашивают: Чёткое механическое объяснение отпускания GIL на I/O вместо заученного правила и приносит здесь полный балл.
Тайп-хинты и mypy стоит внедрять постепенно, сразу защищая каждый уже типизированный модуль от регрессий.
- Начните с мягких настроек mypy, добавляйте более строгие переопределения для отдельных модулей и поставьте гейт в CI для уже чистого кода.
- Типизируйте весь новый код и сначала аннотируйте общее ядро, модели и утилиты, где типы ловят больше всего последующих ошибок.
- Не объединяйте аннотацию тысяч файлов в одно изменение, потому что такой дифф устаревает на ревью, пока продуктовая работа продолжается.
Зачем это спрашивают: Стратегия храповика с помодульной строгостью и энфорсментом в CI и есть практический паттерн, отличающий тех, кто реально проводил эту миграцию.
Типобезопасность обычно утекает через Any и непроверяемые обходные пути вроде кастов и игноров.
- Нетипизированные библиотеки, результаты json.loads и функции без аннотаций порождают Any, который незаметно распространяется и отключает последующие проверки.
- Усильте mypy настройками disallow_untyped_defs и warn_return_any, а внешние данные на границе разбирайте в TypedDict, dataclass или pydantic-модели вместо передачи сырых словарей.
- Требуйте обоснование для кастов и директив type: ignore и периодически ищите их, чтобы удалять ставшие ненужными.
Зачем это спрашивают: Определение распространения Any как режима отказа и типизированных границ как лекарства показывает реальный опыт эксплуатации mypy.
Закрытые вопросы
- 21
Protocol или ABC: как выбираете между структурной и номинальной типизацией для интерфейса?
typestyping - 22
Чем keyword-only аргументы улучшают API и как они объявляются?
api - 23
Объясните разрешение имён LEGB и когда реально нужен nonlocal.
scope - 24
В чём разница между итерируемым объектом и итератором и какой баг вызывает их путаница?
iteration - 25
Как вы проектируете исключения для библиотеки и что добавляет raise from?
designerror-handling - 26
Какой контракт должны вместе соблюдать __eq__ и __hash__ и что ломается при его нарушении?
dunder - 27
Почему сборка строки через += в цикле плоха и что вы используете вместо этого?
- 28
Вы пишете класс Money. Какие dunder-методы реализуете и какие детали важны?
dunder - 29
Как возникают циклические импорты и какие есть варианты их разорвать?
modules - 30
Чем опасна работа, выполняемая при импорте, и что должно лежать под if __name__ == '__main__'?
modules - 31
Threading, multiprocessing или asyncio: проведите меня через ваше решение для новой нагрузки.
asyncconcurrency - 32
Что реально делает await и почему один блокирующий вызов замораживает всё asyncio-приложение?
async - 33
Вам нужно вызвать блокирующую библиотеку из async-кода. Какие варианты?
async - 34
Как asyncio.gather обращается с ошибками и когда вы берёте return_exceptions или TaskGroup?
async - 35
Приведите конкретный пример гонки в многопоточном Python и как вы её устраняете.
concurrencypython - 36
Что ломается при переносе кода с потоков на multiprocessing и чем различаются методы старта fork и spawn?
concurrency - 37
Как вы используете concurrent.futures для параллельной работы и что он даёт сверх сырых потоков?
concurrency - 38
Набросайте схему производитель-потребитель на queue.Queue. Почему очереди хватает без дополнительных замков?
data-structures - 39
Как работают таймауты и отмена в asyncio и что обязана делать воспитанная задача при отмене?
resilienceasync - 40
Когда вы выносите работу из пути запроса в очередь задач вроде Celery и почему эти задачи обязаны быть идемпотентными?
task-queuesidempotencydata-structures - 41
Что дают фикстуры pytest по сравнению с методами setUp и как работают скоупы фикстур?
pytestfixtures - 42
Что такое conftest.py и как не дать ему превратиться в свалку?
pytest - 43
Почему mock.patch('module_a.requests.get') иногда не мокает ничего и как звучит правило?
mocking - 44
MagicMock принимает любой вызов с любыми аргументами. Чем это опасно и как помогает autospec?
mocking - 45
Как pytest.mark.parametrize меняет структуру тестов и зачем нужны ids?
pytestparametrize - 46
Как протестировать, что код поднимает правильное исключение, помимо одного лишь типа?
error-handling - 47
Ваш код зависит от текущего времени и случайности. Как сделать его тесты детерминированными?
testing - 48
Команда добила покрытие тестами до 100 процентов. Что это число гарантирует, а что нет?
coverage - 49
Что меняется при тестировании async-кода в pytest и какая классическая ошибка даёт вечно проходящий тест?
pytestasyncownership - 50
Для кода с базой данных: когда юнит-тесты на SQLite в памяти перестают быть достаточными?
unitdatabasememory - 51
Как TDD реально работает в вашей практике и где вы отступаете от строгого red-green-refactor?
refactoringtdd - 52
В вашем CI флакающие тесты. Каковы обычные первопричины в Python-проектах и как вы устраняете их системно?
flakysystem-designpython - 53
requirements.txt со свободными версиями регулярно ломает сборки. Как выглядит правильная организация зависимостей?
dependencies - 54
Чем стратегия пиннинга версий различается для публикуемой библиотеки и деплоящегося приложения?
deployment - 55
Почему виртуальные окружения не обсуждаются и что реально ломается при установке в системный Python?
system-designpythonvenv - 56
Что такое pyproject.toml и какой фрагментированный бардак он заменил?
packaging - 57
Почему многие проекты кладут пакет в каталог src вместо корня репозитория?
packaging - 58
pip сообщает о конфликте зависимостей между двумя нужными вам пакетами. Как вы его реально разрешаете?
dependenciespip - 59
Как вы настраиваете форматирование и линтинг, чтобы стиль кода никогда не становился темой ревью?
linting - 60
Вам нужно обновить ключевую зависимость через мажорную версию с ломающими изменениями. Опишите ваш процесс.
dependenciesconcurrencyversioning - 61
Как работает инъекция зависимостей через Depends в FastAPI и чем она упрощает тесты?
injectiondependenciestesting - 62
Какую роль играют Pydantic-модели на границах API и почему не гонять сырые словари через сервис?
validation - 63
Когда в Django ORM нужен select_related, а когда prefetch_related, и как ловить пропущенные запросы?
ormqueriesframeworks - 64
Как выкатить Django-миграцию, меняющую колонку в большой живой таблице, без даунтайма?
migrationsschemaframeworks - 65
Почему документация Flask продвигает паттерн фабрики приложения вместо app-объекта уровня модуля?
documentationframeworks - 66
WSGI против ASGI: в чём реальная разница и когда она важна для выбора фреймворка?
wsgi-asgi - 67
Как вы управляете жизненным циклом сессии SQLAlchemy в веб-приложении и что идёт не так при ошибке?
ormsessions - 68
Какие сюрпризы ленивой загрузки есть у SQLAlchemy и как загружать связи осознанно?
ormlazy-loading - 69
Где вы ставите транзакционные границы в веб-приложении и почему автокоммита на каждый стейтмент недостаточно?
transactions - 70
Что делает ответы об ошибках в API хорошо спроектированными и как держать их консистентными по всему Python-сервису?
designapipython - 71
Набросайте, как устроена токеновая аутентификация в сервисе на FastAPI или Django, и назовите классические ошибки реализации.
authtokensframeworks - 72
FastAPI BackgroundTasks или Celery: как выбираете для работы, которая должна случиться после ответа?
task-queuesbackground-jobsframeworks - 73
Offset-пагинация работала нормально, пока таблица не выросла и пользователи не пожаловались на дубли между страницами. Что происходит и что делать?
pagination - 74
Как обработать загрузку большого файла или большой экспорт в Python-сервисе, не исчерпав память?
soft-skillsmemorypython - 75
Эндпоинт тормозит. Проведите меня через профилирование с cProfile и чтение вывода.
profilingendpoints - 76
Какие ошибки заставляют микробенчмарки на timeit вам врать?
ownershipprofiling - 77
Какие дешёвые выигрыши производительности Python вы проверяете до того, как тянуться к C-расширениям и переписываниям?
python - 78
Как вы охотитесь на утечку памяти в Python-процессе с tracemalloc и где утечки обычно прячутся?
memoryconcurrencypython - 79
Память долгоживущего сервиса медленно растёт днями. Как вы атакуете это в продакшне?
memory - 80
Когда functools.lru_cache помогает и какими способами он вам вредит?
cachingfunctools - 81
Ваш числовой цикл на чистом Python медленнее нужного в 100 раз. Какова ваша лестница эскалации?
concurrencypythonescalation - 82
Как вы эффективно используете pdb или breakpoint(), включая случай исключения, которое уже произошло?
error-handling - 83
Баг воспроизводится только в продакшне и никогда локально. Как расследуете без отладчика?
debugging - 84
Тест или джоба падает раз в пятьдесят прогонов. Какова ваша методология прижатия перемежающегося сбоя?
- 85
Вы видите трейсбек с двумя исключениями, разделёнными фразой during handling of the above exception, another exception occurred. Как его читать?
error-handling - 86
Как выглядит хорошая дисциплина логирования в Python-сервисе по сравнению с print-ами?
debuggingloggingpython - 87
Как вы проектируете публичный API Python-модуля или пакета, чтобы он оставался сопровождаемым?
designapipython - 88
Композиция против наследования в Python: где вы проводите черту и что идёт не так в дизайнах, тяжёлых на миксины?
ooppythondesign - 89
Когда утиной типизации достаточно, а когда вы формализуете интерфейс через Protocol или ABC?
typestyping - 90
Объясните EAFP против LBYL на случае, где версия посмотри-перед-прыжком реально бажная.
idioms - 91
Модуль может быть пятью функциями или классом. Как решаете и где подкрадывается оверинжиниринг?
- 92
Вам нужно поменять поведение внутри функции на 500 строк, которую никто до конца не понимает. Как действуете безопасно?
- 93
Помимо стиля, на что вы реально смотрите при ревью Python пул-реквеста?
code-reviewpython - 94
Старший коллега жёстко раскритиковал ваш пул-реквест, и часть критики справедлива. Как отвечаете?
code-review - 95
Вы с коллегой расходитесь в подходе к реализации, и у обоих разумные аргументы. Как разруливаете?
conflict - 96
Посреди спринта вы понимаете, что фича не успеет к дедлайну. Что делаете?
agileestimation - 97
Вы приходите в команду с большой незнакомой Python-кодовой базой. Как выглядят ваши первые две недели?
joinspython - 98
Вам нужно починить баг в легаси-коде без тестов. Какова ваша последовательность?
testing - 99
Как вы решаете, внедрять ли новую фичу Python или инструмент, скажем pattern matching или новый пакетный менеджер, в командную кодовую базу?
decision-makingnpmpython - 100
Ваш деплой только что вызвал инцидент в продакшне. Проведите меня через ваши действия с первого алерта.
deploymentalertingincidents