Skip to content

Вопросы на собеседовании: Python-разработчик

100 реальных вопросов с образцовыми ответами и пояснениями для уровня Middle.

Смотреть пример резюме: Python-разработчик

Тренировка флешкарточками

Интервальное повторение · Hunter Pass

Вопросы

decoratorsfunctools

functools.wraps сохраняет идентичность декорируемой функции и открывает оригинал для интроспекции.

  • Без него __name__ и __doc__ принадлежат врапперу, поэтому трейсбеки, логи, инструменты документации и отладка показывают не ту функцию.
  • Библиотеки, завязанные на метаданные функций, могут работать неверно, включая пиклинг, functools.singledispatch и регистрацию роутов во фреймворках.
  • wraps копирует __name__, __doc__, __module__ и __dict__, а затем задаёт __wrapped__, чтобы inspect.unwrap мог добраться до оригинальной функции.

Зачем это спрашивают: Интервьюер проверяет, отлаживал ли кандидат последствия потерянных метаданных, а не пишет wraps как карго-культ.

resiliencedecorators

Декоратор с аргументами использует вложенные замыкания, чтобы сохранить настройки до момента вызова.

  • Внешняя фабрика получает аргументы вроде times=3 и возвращает настоящий декоратор.
  • Декоратор получает функцию и возвращает враппер, которому через замыкания доступны и функция, и настройки.
  • Скобки обязательны даже при дефолтах, если не поддерживать обе формы через проверку первого позиционного аргумента на callable; более простой вариант состоит в том, чтобы требовать скобки и документировать это.

Зачем это спрашивают: Сильный ответ объясняет цепочку замыканий и зачем нужен лишний уровень вызова, а не воспроизводит заученную вложенность.

decorators

Сложенные декораторы применяются от функции наружу, а при вызове начинают выполняться в обратном порядке.

  • Применение идёт снизу вверх: ближайший к def декоратор первым оборачивает функцию, а каждый следующий оборачивает предыдущий результат.
  • Выполнение идёт сверху вниз: первым срабатывает самый внешний враппер и делегирует вызов внутрь.
  • Порядок влияет на поведение: кеш снаружи проверки авторизации может кешировать ответы для неавторизованных пользователей, а обратный порядок пропускает каждый кешированный результат через авторизацию.

Зачем это спрашивают: Пример с авторизацией и кешем показывает, что кандидат понимает порядок как реальную проблему корректности, а не тривию.

error-handling

__exit__ всегда получает возможность выполнить очистку и решает, выйдет ли исключение за пределы блока with.

  • Он получает тип исключения, значение и трейсбек либо три значения None, если блок завершился нормально.
  • Истинное возвращаемое значение подавляет исключение и продолжает выполнение после блока with, как это делает contextlib.suppress.
  • Возврат None или False пропускает исключение дальше, но очистка в __exit__ в любом случае выполняется.

Зачем это спрашивают: Деталь о том, что возвращаемое значение управляет подавлением, отделяет тех, кто реализовывал контекст-менеджеры, от тех, кто их только использует.

generatorscontext-managers

contextlib.contextmanager сопоставляет две стороны единственного yield со входом в контекст и выходом из него.

  • Код до yield работает как __enter__, а отданное значение становится целью после as.
  • Код после yield работает как __exit__, поэтому yield нужно оборачивать в try/finally, чтобы гарантировать очистку при исключении в теле with.
  • Исключение вбрасывается в генератор в точке yield, а перехват без повторного raise подавляет его так же, как возврат True из __exit__.

Зачем это спрашивают: Требование try/finally и есть классический баг, на который охотится вопрос, а сильные кандидаты называют механизм вбрасывания в генератор.

ci-cdgenerators

Генератор приостанавливает и возобновляет один сохранённый фрейм выполнения, поэтому данные можно обрабатывать по одному элементу.

  • Вызов генераторной функции не выполняет её тело, а возвращает объект-генератор с приостановленным фреймом.
  • Каждый next() продолжает выполнение до yield, отдаёт значение и снова останавливается, сохраняя локальные переменные и указатель инструкции.
  • Пайплайн вроде parse(filter(read_lines(f))) проводит каждый элемент через все стадии и сохраняет постоянное потребление памяти даже для многогигабайтного лога.

Зачем это спрашивают: Интервьюер слушает ментальную модель приостановленного фрейма и конкретный стриминговый кейс, а не формулу yield возвращает несколько значений.

generatorscomprehensions

Список подходит для повторно используемых материализованных данных, а генератор для ленивого однопроходного потребления.

  • Списковое включение сразу строит весь результат, поэтому подходит для проверки длины, нескольких проходов, индексации и сортировки.
  • Генераторное выражение отдаёт значения лениво и хорошо сочетается с sum или any, где короткое замыкание позволяет избежать лишней работы.
  • Генератор не подходит, если потребитель проходит по данным дважды: второй проход молча не вернёт элементов и не вызовет ошибку.

Зачем это спрашивают: Называние бага с тихим исчерпанием показывает, что кандидат на нём обжигался, а именно такую практическую глубину и прощупывают.

generators

yield from делегирует полный протокол генератора, а не только передаёт выдаваемые значения.

  • При простой итерации он похож на цикл for с yield, но send() и throw() от потребителя передаются в субгенератор.
  • Возвращаемое значение субгенератора становится значением выражения yield from, что позволяет генератору давать и поток, и финальный результат.
  • На этой передаче возвращаемых значений строились корутинные фреймворки до async, тогда как ручной цикл ломает двустороннюю связь.

Зачем это спрашивают: Упоминание делегирования send и захвата возвращаемого значения отличает настоящее понимание от сахара над циклом.

hypothesis-testinglambda

Лямбды в цикле захватывают одну общую переменную, поэтому при позднем вызове видят её последнее значение.

  • Замыкания захватывают переменные, а не снимки их значений, и после цикла общая переменная содержит последний элемент.
  • Аргумент по умолчанию вроде lambda x=x: ... привязывает текущее значение в момент определения; вместо этого functools.partial может сохранить его явно.
  • Без такой привязки колбеки или словари обработчиков, созданные в цикле, заставляют каждый обработчик работать с последним элементом.

Зачем это спрашивают: Интервьюер хочет услышать позднее связывание как причину и идиому с аргументом по умолчанию как фикс, в идеале с контекстом регистрации колбеков.

gotchas

Аргументы по умолчанию вычисляются один раз при определении функции, что опасно для изменяемых объектов, но полезно для намеренных снимков значений.

  • def f(items=[]) повторно использует один список во всех вызовах, поэтому добавленные значения протекают между ними.
  • Стандартное исправление использует сентинел None и выражение items = [] if items is None else items внутри функции.
  • Привязка при определении может намеренно сохранить значение переменной цикла или закешировать дорогой поиск, хотя для кеширования лучше использовать явные механизмы.

Зачем это спрашивают: Знание механизма работает в обе стороны, и сильный кандидат может назвать легитимное применение вычисления при def, а не только баг.

descriptorstyping

Дескрипторы централизуют управление атрибутами через методы типа классового атрибута.

  • Дескриптор определяет __get__, __set__ или __delete__, а доступ к атрибуту экземпляра направляется через эти методы посредством класса.
  • property является встроенным дескриптором, который связывает чтение и запись с функциями; функции тоже являются дескрипторами, чей __get__ создаёт связанные методы.
  • Собственный дескриптор полезен, когда логика вроде валидации полей или преобразования единиц повторяется во многих атрибутах и иначе потребовала бы множества похожих property.

Зачем это спрашивают: Связывание property и связанных методов с одним протоколом показывает системное понимание, а критерий переиспользования показывает практическое суждение.

slots

__slots__ меняет динамические атрибуты экземпляра на фиксированное хранилище и снижает расход памяти на каждый объект.

  • Он заменяет __dict__ каждого экземпляра хранилищем перечисленных атрибутов, заметно сокращая память и немного ускоряя доступ к атрибутам.
  • Произвольные атрибуты в рантайме недоступны, слабые ссылки требуют явного слота __weakref__, а родитель без слотов возвращает __dict__ при наследовании.
  • Это оправдано для миллионов экземпляров вроде узлов AST или точек тиковых данных, но обычным доменным классам чаще важнее гибкость.

Зачем это спрашивают: Критерий окупаемости через массовые количества экземпляров отделяет осознанное применение от преждевременной оптимизации.

decoratorsdataclasses

dataclass генерирует типичные методы объекта данных из аннотированных полей и даёт безопасные средства для неизменяемости и дефолтов.

  • Он создаёт __init__, __repr__ и __eq__, а при необходимости __hash__ и методы сравнения, убирая повторяющиеся конструкторы из одних присваиваний.
  • frozen=True запрещает присваивание атрибутов, что позволяет хешировать экземпляры и безопасно использовать их как ключи словаря.
  • field(default_factory=list) вызывает фабрику для каждого экземпляра и избегает бага с общим изменяемым дефолтом в виде обычного списка.

Зачем это спрашивают: Привязка default_factory к ловушке изменяемых дефолтов показывает, что кандидат понимает, почему API устроен именно так.

conflict

== сравнивает значения через __eq__, а is проверяет, указывают ли две ссылки на один объект.

  • Равные строки, построенные в рантайме, могут быть разными объектами, хотя == вернёт True.
  • CPython интернирует маленькие целые числа и некоторые строки времени компиляции, но поведение меняется для склеенных или пользовательских строк и между реализациями.
  • Используйте is для синглтонов вроде None, True, False и собственных сентинел-объектов, но не для сравнения значений с опорой на интернирование.

Зачем это спрашивают: Ограничение is семантикой идентичности и синглтонами с объяснением ненадёжности проверок интернирования и есть ожидаемый зрелый ответ.

copying

Поверхностное копирование дублирует только контейнер, а глубокое рекурсивно копирует его граф объектов.

  • copy.copy создаёт новый внешний объект, но его элементы остаются прежними ссылками, поэтому изменение вложенного списка через копию меняет и оригинал.
  • copy.deepcopy рекурсивно копирует граф и использует словарь memo для обработки циклов.
  • Поверхностная копия конфига может распространить вложенное изменение одного тенанта на всех, но глубокое копирование огромных структур в горячих путях бывает дороже переработки кода без мутаций.

Зачем это спрашивают: Интервьюеру нужны механизм разделяемых ссылок плюс понимание реальной цены deepcopy, а не голые определения.

gc

CPython нужен сборщик циклов, потому что один подсчёт ссылок не освобождает недостижимые объекты, которые ссылаются друг на друга.

  • Подсчёт ссылок освобождает объект при нулевом счётчике, но участники цикла удерживают счётчики друг друга выше нуля.
  • Поколенческий циклический сборщик периодически находит такие недостижимые группы; __del__ мешает этому из-за неоднозначного порядка финализации умирающего цикла.
  • Исторически такие циклы не собирались, а сегодня финализаторы могут сработать в неожиданном порядке, поэтому ресурсы лучше очищать через контекст-менеджеры или weakref.finalize.

Зачем это спрашивают: Пример с циклом и предпочтение контекст-менеджеров вместо __del__ показывают операциональное понимание управления памятью.

gil

GIL защищает внутреннее состояние интерпретатора CPython, но не обеспечивает потокобезопасность операций приложения.

  • Он позволяет только одному потоку выполнять байткод Python в каждый момент и защищает от повреждения состояние интерпретатора, например счётчики ссылок.
  • Действия из нескольких байткодов, такие как проверка с последующим обновлением или += для общего атрибута, всё равно могут перемежаться и вызывать гонки и потерю обновлений.
  • GIL освобождается на блокирующем I/O и внутри многих C-расширений, поэтому потоки могут ускорять I/O-bound нагрузки.

Зачем это спрашивают: Различие между безопасностью интерпретатора и потокобезопасностью прикладного кода и есть ключевое заблуждение, которое вопрос призван вскрыть.

concurrencypython

Потоки совмещают блокирующие ожидания в I/O-bound работе, но выполнение CPU-bound кода Python сериализуется из-за GIL.

  • Поток освобождает GIL при блокирующем чтении сокета или обращении к базе, позволяя другим потокам работать и повышая пропускную способность при одновременных ожиданиях.
  • CPU-bound потоки не ждут, а конкурируют за один GIL, поэтому работа сериализуется и может замедлиться из-за расходов на переключение.
  • Для тяжёлых вычислений подходят multiprocessing, C-расширения вроде numpy, которые освобождают GIL внутри, или другой рантайм.

Зачем это спрашивают: Чёткое механическое объяснение отпускания GIL на I/O вместо заученного правила и приносит здесь полный балл.

typing

Тайп-хинты и mypy стоит внедрять постепенно, сразу защищая каждый уже типизированный модуль от регрессий.

  • Начните с мягких настроек mypy, добавляйте более строгие переопределения для отдельных модулей и поставьте гейт в CI для уже чистого кода.
  • Типизируйте весь новый код и сначала аннотируйте общее ядро, модели и утилиты, где типы ловят больше всего последующих ошибок.
  • Не объединяйте аннотацию тысяч файлов в одно изменение, потому что такой дифф устаревает на ревью, пока продуктовая работа продолжается.

Зачем это спрашивают: Стратегия храповика с помодульной строгостью и энфорсментом в CI и есть практический паттерн, отличающий тех, кто реально проводил эту миграцию.

typing

Типобезопасность обычно утекает через 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