Подготовка к Python backend интервью

Вопросы и ответы на Python собеседовании 2026

Сфокусированная подборка вопросов для Python backend интервью. Используйте каждый ответ как структуру устного ответа: концепт, пример и компромиссы.

База и модель данных Python

1Какие встроенные типы данных есть в Python и какие из них изменяемые?

В Python есть скалярные типы NoneType, bool, int, float и complex; текстовые и бинарные типы str, bytes и bytearray; последовательности list, tuple и range; коллекции dict, set и frozenset. Ключевое различие для собеседования — mutability: list, dict, set и bytearray меняются на месте, а числа, bool, str, bytes, tuple, range, frozenset и None неизменяемы. Важный нюанс: tuple неизменяем как контейнер, но может хранить ссылки на изменяемые объекты.

2Чем list, tuple, set и dict отличаются в реальном коде?

list — упорядоченная изменяемая последовательность, удобная для накопления результатов и данных, которые меняются. tuple — упорядоченная неизменяемая структура для фиксированных записей и возврата нескольких значений. set хранит уникальные hashable-объекты и подходит для membership checks и deduplication. dict отображает hashable-ключи в значения и обычно используется для индексов, lookup и JSON-подобных данных. В современных версиях dict сохраняет порядок вставки, но его главная роль — доступ по ключу.

3Чем is отличается от ==?

== проверяет равенство по значению, обычно через __eq__. is проверяет идентичность: две ссылки указывают на один и тот же объект. В production-коде is обычно используют для singleton-проверок, например value is None, или для специальных sentinel-объектов. Нельзя полагаться на кеширование маленьких чисел или interning строк: в маленьких примерах is может случайно сработать, но семантически это неправильная проверка.

4Что такое hashable-объект и почему это важно?

Hashable-объект имеет стабильный hash на протяжении жизни и равенство, согласованное с этим hash. Это важно, потому что ключи dict и элементы set зависят от hash. Большинство неизменяемых built-ins hashable, но неизменяемый контейнер не делает вложенные объекты hashable автоматически: tuple со списком внутри не hashable. Если объект используется как ключ, поля, определяющие его идентичность, не должны меняться после вставки.

5Как работают срезы, отрицательные индексы и шаг?

Срез sequence[start:stop:step] включает start, исключает stop и движется с шагом step. Пропущенные границы выбираются в зависимости от направления шага. Отрицательные индексы считаются с конца, поэтому s[-1] — последний элемент. Отрицательный step идет справа налево, а s[::-1] возвращает перевернутую последовательность. Нужно помнить, что срез часто создает новый объект, поэтому он выразительный, но не бесплатный на больших данных.

Функции и области видимости

6Как Python передает аргументы в функции?

Python передает ссылки на объекты через присваивание; это часто называют call by sharing. Параметры функции становятся локальными именами, связанными с теми же объектами, которые передал вызывающий код. Если объект изменяемый, функция может изменить его, и вызывающий код это увидит. Если функция просто присвоит параметру другое значение, изменится только локальная привязка. Хороший ответ включает оба примера: append в переданный list меняет его, а items = [] внутри функции не заменяет список у вызывающего кода.

7Почему изменяемые аргументы по умолчанию опасны?

Значения по умолчанию вычисляются один раз при определении функции, а не при каждом вызове. Поэтому list или dict в default может разделяться между всеми вызовами и случайно хранить состояние. Безопасный паттерн: def f(items=None), затем внутри создать новый список, если items is None. Иногда общее состояние нужно специально, например для маленького cache, но тогда это должно быть явно и понятно читателю.

8Для чего нужны *args и **kwargs?

*args собирает лишние позиционные аргументы в tuple, а **kwargs собирает лишние именованные аргументы в dict. Они полезны для wrappers, decorators, гибких API и проброса вызовов дальше. При вызове *iterable распаковывает позиционные значения, а **mapping — keyword values. Компромисс в читаемости: явные параметры лучше для стабильного публичного API, а args/kwargs лучше на границах совместимости и forwarding.

9Как Python ищет имена по правилу LEGB?

Python ищет имя в Local, Enclosing, Global и Builtins. Чтение имени идет по этому порядку. Присваивание внутри функции обычно создает локальную переменную, поэтому можно получить UnboundLocalError, если прочитать имя до локального присваивания. global направляет присваивание в модульную область, nonlocal — во внешнюю функцию. В production-коде их лучше использовать редко, потому что они усложняют поток состояния.

10Что такое closure и late binding?

Closure — функция, которая сохраняет доступ к переменным из внешней области после завершения внешней функции. Это полезно для decorators, callback factories и функций с конфигурацией. Late binding означает, что свободная переменная ищется при вызове внутренней функции, а не при ее создании. Поэтому lambda в цикле может увидеть последнее значение счетчика. Значение фиксируют через default argument, например lambda i=i: i, или через отдельную factory-функцию.

ООП, итерация и ресурсы

11Чем переменная класса отличается от переменной экземпляра?

Переменная класса хранится на объекте класса и разделяется экземплярами, пока экземпляр не перекроет ее собственным атрибутом. Переменная экземпляра хранится на конкретном объекте и обычно задается как self.name в __init__. Неизменяемые class constants обычно безопасны, но изменяемые class attributes часто становятся багом, если их случайно используют как состояние экземпляра. В ответе полезно упомянуть порядок поиска: сначала instance, потом class и ancestors.

12Как работают inheritance, MRO и super()?

Python ищет атрибуты по Method Resolution Order, который можно посмотреть через ClassName.__mro__. В одиночном наследовании все просто, но при множественном наследовании Python использует C3 linearization, чтобы получить согласованный порядок. super() означает не просто 'вызвать родителя', а 'вызвать следующую реализацию в MRO'. Это позволяет cooperative multiple inheritance, если методы принимают совместимые аргументы и каждый класс корректно вызывает super().

13Чем отличаются iterable, iterator и generator?

Iterable — объект, который возвращает iterator через iter(), например list или file object. Iterator хранит состояние обхода, реализует __next__ и вызывает StopIteration при завершении. Generator — удобный способ создать iterator через yield или generator expression. Генераторы экономят память, потому что лениво производят значения, но обычно одноразовые и не поддерживают индексацию как list.

14Что такое context manager и зачем нужен with?

Context manager задает поведение входа и выхода через __enter__ и __exit__, либо создается через contextlib.contextmanager. with делает управление ресурсами явным и надежным: файлы закрываются, lock освобождается, транзакция commit/rollback выполняется даже при исключениях. __exit__ получает информацию об исключении и может подавить его, вернув true, но подавлять ошибки стоит осторожно.

15Чем shallow copy отличается от deep copy?

Shallow copy создает новый внешний контейнер, но оставляет ссылки на те же вложенные объекты. Deep copy рекурсивно копирует вложенные объекты, где это возможно. Для простых плоских списков shallow copy часто достаточно. Для вложенных изменяемых структур shallow copy может разделять состояние и удивлять. Deep copy дороже и может быть неверен для ресурсов: открытых файлов, sockets, database sessions или объектов с важной identity-семантикой.

Runtime, async и production-код

16Что такое GIL и как он влияет на backend-код?

В стандартном CPython Global Interpreter Lock позволяет только одному потоку выполнять Python bytecode в конкретный момент. Поэтому threads обычно не ускоряют CPU-bound Python-код. Но threads полезны для I/O-bound задач, потому что GIL освобождается во время многих операций ожидания. Для CPU-heavy работы используют multiprocessing, native extensions, отдельные сервисы или free-threaded builds после проверки совместимости. Хороший backend-ответ связывает GIL с типом нагрузки, а не говорит просто 'threads бесполезны'.

17Когда выбирать threads, processes или asyncio?

Threads подходят для blocking I/O и библиотек только с синхронным API. Processes выбирают для CPU-bound работы, изоляции или устойчивости к падениям, принимая overhead сериализации и памяти. asyncio подходит для большого числа concurrent I/O operations с async-compatible библиотеками: async database drivers, HTTP clients, queues. Практический выбор зависит от framework и dependencies: async endpoint с blocking calls может стать хуже, а не лучше.

18Для чего async/await полезны в Python web services?

async и await дают cooperative concurrency. Coroutine возвращает управление event loop, когда ожидает I/O, и в это время могут выполняться другие задачи. Это полезно для API, которые ждут database, HTTP calls, queues или sockets. Это не ускоряет CPU-bound код: тяжелые вычисления блокируют event loop, если не вынести их в worker, process или native code. В FastAPI async endpoint помогает только если весь call path избегает blocking operations.

19Как правильно обрабатывать exceptions в production Python?

Перехватывайте самый конкретный exception, который можете обработать, и делайте это на слое, который может восстановиться, добавить контекст или перевести ошибку в domain error. Избегайте bare except и молчаливого swallowing: это скрывает потерю данных и operational problems. Используйте raise для повторного возбуждения и raise NewError(...) from exc для сохранения причины. На границах системы нужны полезные logs, корректные HTTP errors и сообщения без утечки secrets.

20Как структурировать и тестировать production Python service?

Отделяйте domain logic от framework и infrastructure code, чтобы основное поведение тестировалось без сервера и базы. Используйте configuration из environment или typed settings, logging вместо print, dependency injection на границах и явные timeouts для network calls. В тестах сочетайте unit tests для чистой логики, integration tests для database/external boundaries и несколько end-to-end checks для критических flows. Mock используйте на понятных границах, а проверяйте observable behavior, не private implementation details.