List хранит порядок и допускает дубликаты, set хранит только уникальные значения без порядка.
Проверка вхождения (`in`) у set — O(1) за счёт хеш-таблицы, у list — O(n) линейный перебор.
Из практики: set использую для проверки уникальности идентификаторов в API-ответе — `assert len(ids) == len(set(ids))`. Список беру там, где важен порядок элементов.
Частые ошибки
Используют list там, где нужна уникальность.
Дополнительные вопросы
Когда в тесте важнее порядок, а когда уникальность?
Что такое неизменяемые объекты (immutable)?
Junior
Immutable объекты нельзя изменить «на месте» — вместо этого операция создаёт новое значение. Примеры: строки, кортежи, int, frozenset.
Это упрощает отладку и безопасно при конкурентном доступе — несколько потоков могут читать immutable объект без блокировок.
Из практики: строки как параметры тестовых фикстур — типичный immutable. Это страхует от случайного изменения тестовых данных при параллельном запуске.
Частые ошибки
Путают изменение ссылки и изменение объекта.
Дополнительные вопросы
Какие immutable типы используешь чаще всего?
Как работают исключения?
Junior
Исключение прерывает обычный поток выполнения и передаёт управление в обработчик try/except (Python) или try/catch (Java).
Ловлю только ожидаемые, конкретные исключения — никогда не `except Exception` без крайней необходимости. Добавляю понятный контекст в сообщение об ошибке.
Из практики: в API-клиентах ловлю только `ConnectionError` и `Timeout`, а не все исключения — иначе тест проглатывает неожиданные ошибки и остаётся зелёным, когда должен упасть.
Частые ошибки
Глушат все ошибки общим обработчиком.
Дополнительные вопросы
Как проверишь исключение в тесте?
В чём разница между deepcopy и copy (Python)?
Junior
copy делает поверхностную копию: вложенные объекты остаются общими — оба объекта ссылаются на одни и те же вложенные структуры.
deepcopy копирует объект рекурсивно вместе со всеми вложенными структурами — полностью независимая копия.
Из практики: при создании тестовых данных через фабрику передаю deepcopy вложенных структур — иначе все тесты модифицируют один объект и падают случайным образом.
Частые ошибки
Думают, что copy изолирует вложенные структуры.
Дополнительные вопросы
Когда shallow copy всё же уместен?
Как использовать параметризацию?
Junior
Параметризация в Pytest позволяет запускать один тест на наборе входных данных без копипаста.
Обычно использую `@pytest.mark.parametrize`, чтобы в отчёте видно было каждый набор данных как отдельный тест-кейс.
Из практики: параметризация быстро расширяет покрытие и делает регрессию компактнее.
Частые ошибки
Путают параметризацию с циклой внутри теста: вместо явного цикла лучше использовать @parametrize для отчётности по каждому случаю отдельно.
Неправильно передают параметры (например, список вместо отдельных значений), из-за чего Pytest видит один аргумент.
Дополнительные вопросы
Можно ли параметризовать фикстуры аналогично тестам?
Как в отчёте тестов отобразятся разные параметры (имена тестов)?
Как в Pytest пропускать тесты?
Junior
Безусловный пропуск — декоратор `@pytest.mark.skip(reason="...")`; по условию — `@pytest.mark.skipif(condition, reason="...")` (например, версия Python, ОС, наличие зависимости).
Динамически внутри теста — вызов `pytest.skip("...")`, а `pytest.importorskip("module")` пропускает тест, если модуль не установлен.
Всегда указывай `reason`, чтобы в отчёте было видно: это ожидаемый пропуск или проблема.
Из практики: пропуск без причины быстро превращается в «тихую дыру» в покрытии.
Частые ошибки
Злоупотребляют пропусками вместо удаления или фикса починенного теста: в итоге тесты навсегда остаются пропущенными.
Не указывают причину пропуска, затрудняя понимание, почему тест не выполняется.
Дополнительные вопросы
Как пропустить весь класс тестов или всю модульную группу сразу?
Чем отличается xfail от skip в Pytest?
Как можно проверить, что в тестах были выброшены исключения?
Junior
Проверяю исключения через `pytest.raises`, чтобы явно зафиксировать ожидаемый тип ошибки.
Дополнительно сравниваю текст или атрибуты исключения, если важно подтвердить конкретный сценарий ошибки.
Из практики: такая проверка отлично ловит случаи, когда код падает «не тем» исключением.
Частые ошибки
Пишут try/except внутри теста вместо использования pytest.raises, что усложняет код теста.
Забывают проверить, что исключение действительно произошло, либо ловят слишком широкий Exception, пропуская ошибку.
Дополнительные вопросы
Как проверить текст сообщения исключения через pytest.raises?
Можно ли убедиться, что исключение не было выброшено в тесте (обратная ситуация)?
Что такое тип данных?
Junior
Тип данных определяет множество допустимых значений и операций над ними.
В Python типы динамические: тип привязан к объекту, не к переменной. Одна переменная может указывать на объекты разных типов в разное время.
Из практики: динамическая типизация упрощает написание тестов, но без type hints легко передать строку туда, где ожидается int. Покрываю это аннотациями типов и mypy в pre-commit.
Частые ошибки
Путают тип переменной с типом объекта: в Python переменные не имеют фиксированного типа.
Игнорируют особенности типов.
Дополнительные вопросы
Как узнать тип объекта во время выполнения?
Какие типы относятся к неизменяемым?
Какие типы данных есть в Python?
Junior
В Python часто используют числа, строки, булевы значения, списки, словари, множества и кортежи.
Важно понимать, какие типы изменяемые, а какие нет, чтобы избежать побочных эффектов в коде и тестах.
Из практики: большинство «странных» багов в Python-коде связано именно с изменяемостью объектов.
Частые ошибки
Забывают про неизменяемые коллекции (tuple, frozenset).
Не различают текст (str) и байтовые строки (bytes).
Дополнительные вопросы
Что такое изменяемые и неизменяемые типы?
Чем отличается list от tuple?
В чем отличие `==` и `is`?
Junior
`==` сравнивает значения, а `is` проверяет, ссылаются ли переменные на один и тот же объект в памяти.
Для сравнения содержимого почти всегда нужен `==`, а `is` обычно применяю для `None` и диагностики ссылочной идентичности.
Из практики: путаница между `==` и `is` регулярно приводит к трудноуловимым багам в тестах.
Частые ошибки
Используют `is` вместо `==` для сравнения значений, получая ложные результаты.
Не учитывают, что малые числа и короткие строки кешируются и `is` случайно может дать True.
Дополнительные вопросы
Как переопределить поведение оператора == в своём классе?
Почему сравнение больших чисел через `is` опасно?
Как в Python передаются аргументы в функцию?
Junior
В Python аргументы передаются как ссылки на объекты: функция работает с тем же объектом, который передали снаружи.
Если объект изменяемый, изменения внутри функции будут видны снаружи; если просто переприсвоить имя параметра, внешний объект не изменится.
Из практики: эту разницу важно помнить при работе со списками и словарями в тестовых фикстурах.
Частые ошибки
Говорят «по ссылке» или «по значению» без уточнения: фактически семантика называется pass-by-object-reference.
Путают изменение объекта и переназначение локальной переменной (не меняет оригинал).
Дополнительные вопросы
Как передать копию списка в функцию, чтобы он не изменился снаружи?
Что произойдёт при изменении неизменяемого объекта внутри функции?
Что такое `*args` и `**kwargs`?
Junior
`*args` собирает произвольное число позиционных аргументов в кортеж, `**kwargs` — именованные аргументы в словарь.
Это удобно для гибких функций и обёрток, где заранее неизвестен полный набор параметров.
Из практики: в тестовых утилитах `**kwargs` помогает расширять API без ломающих изменений.
Частые ошибки
Заменяют имена *args/**kwargs другими, теряя общепринятость.
Путают распаковку `*` в вызове и объявлении функции.
Дополнительные вопросы
Как можно объединить *args и обычные параметры?
Что произойдёт, если передать лишние именованные параметры функции без **kwargs?
В чём разница между списком и кортежем?
Junior
Список (list) — изменяемая последовательность, элементы можно добавлять, удалять, изменять. Кортеж (tuple) — неизменяемая последовательность: после создания элементы и длина фиксированы.
Кортеж чуть быстрее создаётся и занимает меньше памяти. Кортеж можно использовать как ключ словаря (если элементы хешируемы), список — нельзя.
Из практики: конфигурационные данные (URL-эндпоинты, ожидаемые статусы) храню в кортежах — случайное изменение в процессе параллельного выполнения тестов исключено.
Частые ошибки
Предполагают, что кортеж быстрее списка всегда (разница минимальна, главное – неизменяемость).
Пытаются изменить элемент кортежа напрямую, вызывая ошибку.
Дополнительные вопросы
Когда вы предпочтёте использовать кортеж вместо списка?
Как можно изменить кортеж косвенно (обходным путём)?
Можно ли использовать кортеж как ключ словаря? Если да, каким должно быть содержимое?
Junior
Кортеж пригоден как ключ словаря, если содержит только хешируемые (неизменяемые) элементы — строки, числа, другие кортежи.
Если внутри кортежа есть список или словарь — он не хешируем и не может быть ключом; Python поднимет `TypeError: unhashable type`.
Из практики: комбинацию (user_role, feature_flag) использую как ключ словаря для кэширования ответов в параметризованных тестах — кортеж идеально подходит для составных ключей.
Частые ошибки
Полагают, что любой кортеж автоматически хешируем.
Изменяют содержимое объекта, который был ключом.
Дополнительные вопросы
Почему список нельзя использовать в качестве ключа?
Как проверить, это ли объект хешируемым?
Что такое `lambda`-функция?
Junior
`lambda` — это короткая безымянная функция для простых случаев.
Я использую её, когда действие маленькое и код от этого становится короче и читабельнее.
Из практики: если логика сложная, лучше обычная функция с понятным именем.
Частые ошибки
Пытаются написать многострочные операции в lambda, хотя она ограничена выражением.
Используют lambda там, где лучше подошла бы именованная функция (затрудняет чтение).
Дополнительные вопросы
Как передать lambda-функцию в другую функцию, например, sorted?
В чём ограничения lambda-функций по сравнению с обычными?
Что такое ООП? Какие есть принципы?
Junior
Объектно-ориентированное программирование — парадигма, основанная на объектах, которые объединяют данные (атрибуты) и поведение (методы).
Четыре классических принципа ООП: инкапсуляция (скрытие внутреннего состояния), наследование (расширение классов), полиморфизм (единый интерфейс для разных типов) и абстракция (сокрытие деталей реализации за интерфейсом).
Из практики: в автотестах ООП помогает делать код модульным — Page Object использует инкапсуляцию, базовый класс теста — наследование, а паттерны Factory/Strategy — полиморфизм.
Частые ошибки
Называют принципы, но не понимают их на практике (например, путают полиморфизм с перегрузкой).
Сводят ООП только к синтаксису классов, забывая про дизайн и отношения объектов.
Дополнительные вопросы
Какой принцип ООП нарушается при «God object»?
Что такое SOLID и как он связан с принципами ООП?
Что такое итератор? Что такое итерируемый объект?
Junior
Итерируемый объект — это объект, по которому можно пройти в цикле, обычно через метод `__iter__`.
Итератор — объект, который возвращает элементы по одному через `__next__` и помнит текущее состояние обхода.
Из практики: понимание разницы помогает писать более экономичные проверки на больших наборах данных.
Частые ошибки
Смешивают понятия: каждый итератор это итерируемым, но не каждый итерируемый объект сам это итератором.
Забывают вызывать iter() для получения итератора и напрямую используют __next__ на итерируемом объекте, который не это итератором.
Дополнительные вопросы
Как сделать свой объект итерируемым?
Почему цикл for не вызывает StopIteration явным образом в коде?
Как группировать тесты?
Middle
Группировка тестов в Pytest осуществляется через маркеры: `@pytest.mark.smoke`, `@pytest.mark.regression`, `@pytest.mark.slow`. Регистрируются в `pytest.ini` или `pyproject.toml`.
Запуск группы: `pytest -m smoke`, комбинации: `pytest -m 'smoke and not slow'`. Тесты также группируются физически — по файлам и по классам.
Из практики: разделяю тесты метками smoke, regression, integration — в CI запускаю только smoke на каждый коммит, полную регрессию ночью. Это сокращает цикл обратной связи с 40 до 5 минут.
Частые ошибки
Ограничиваются только названием файлов или классов, не используя markers, что затрудняет выборочную прогонку.
Не регистрируют кастомные метки в pytest.ini, получая предупреждения (в Pytest надо объявлять свои метки).
Дополнительные вопросы
Как запустить только тесты с определённой меткой?
Можно ли в Pytest выполнить набор тестов в определённом порядке (или это антипаттерн)?
Можно ли запускать тесты параллельно?
Middle
С плагином pytest-xdist тесты запускаются параллельно в нескольких процессах: `pytest -n auto` (по числу ядер) или `pytest -n 4` (4 процесса).
Важно, чтобы тесты были независимы — общие данные без изоляции вызовут гонки и непредсказуемые падения. Каждый тест должен создавать уникальные данные.
Из практики: переход на pytest-xdist сократил время регрессии с 40 минут до 12. Главное условие — уникальные UUID в тестовых данных, чтобы тесты не конкурировали за одни записи.
Частые ошибки
Ожидают, что Pytest параллелит из коробки (по умолчанию он идёт последовательно).
Запускают зависящие друг от друга тесты параллельно, получая плавающие (нестабильные) падения.
Дополнительные вопросы
Как обеспечить изоляцию данных при параллельном запуске тестов?
Поддерживает ли Pytest параллельность потоков внутри одного теста?
Что такое аннотации типов (type hints) в Python и зачем они нужны?
Middle
Аннотации типов — синтаксис Python для указания ожидаемых типов переменных, параметров и возвращаемых значений: `def get_user(id: int) -> User:`.
Они не влияют на выполнение, но помогают статическим анализаторам (mypy) и IDE находить ошибки несоответствия типов до запуска кода.
Из практики: добавляю аннотации типов в API-клиенты и фабрики тестовых данных — mypy в pre-commit ловит ошибки типов раньше, чем они проявятся в CI.
Частые ошибки
Считают, что аннотации принудительно меняют тип переменных (на самом деле это подсказки).
Не поддерживают актуальность аннотаций, что вводит в заблуждение читающих код.
Дополнительные вопросы
Какие инструменты проверяют аннотации типов в Python?
Как задать тип обобщённого контейнера (Generics) в аннотациях?
Почему мутабельные объекты внутри кортежа могут меняться, если кортеж считается неизменяемым?
Middle
Да, внутри кортежа могут быть изменяемые объекты. Неизменяемость кортежа означает, что ссылки на элементы фиксированы — нельзя заменить элемент на другой объект.
Но если элемент — изменяемый контейнер (список, словарь), его содержимое можно менять. Хешируемость кортежа при этом теряется.
Из практики: помещал список в кортеж, считая данные 'защищёнными' — другая часть кода модифицировала этот список. Теперь в кортежах для ключей использую только хешируемые типы.
Частые ошибки
Считают, что наличие изменяемого элемента делает весь кортеж изменяемым.
Путают понятийную неизменяемость с полным запретом мутаций внутренних объектов.
Дополнительные вопросы
Может ли изменяемый объект быть ключом словаря?
Как влияет на хеш кортежа наличие внутри изменяемых объектов?
Что быстрее работает, словарь или список?
Middle
Для поиска по ключу словарь значительно быстрее: в словаре это амортизированно O(1) за счёт хеш-таблицы, в списке — O(n) линейный перебор.
Список эффективнее при последовательном обходе всех элементов и индексированном доступе по позиции — там O(1). Для проверки вхождения (`in`) у set тоже O(1).
Из практики: если часто ищете по ключу — словарь или set; если важен порядок и доступ по индексу — список. Неправильный выбор структуры легко замедляет тесты на больших данных.
Частые ошибки
Сравнивают скорости вне контекста: разные структуры оптимальны для разных задач.
Забывают про расход памяти: словарь требует больше памяти на хранение хеш-таблицы.
Дополнительные вопросы
В каких случаях список предпочтительнее словаря?
Какова сложность добавления элемента для списка и словаря?
Использование слайса — это копирование? Если да, то какое?
Middle
Срез списка в Python создаёт новый список, то есть выполняется копирование диапазона элементов.
Это поверхностная копия: вложенные изменяемые объекты не дублируются, копируются их ссылки.
Из практики: из-за этого часто появляются скрытые побочные эффекты в тестах с вложенными структурами.
Частые ошибки
Считают срез «представлением» на оригинальный список (на самом деле это новый объект).
Не учитывают, что изменения в элементах (если они изменяемые) отразятся и в оригинале, ведь копируются ссылки.
Дополнительные вопросы
Как сделать глубокую копию списка с вложенными структурами?
Что вернёт срез [: ] без указания границ?
Когда применение анонимных функций оправдано?
Middle
Анонимные функции (lambda) оправданы когда функция очень простая, используется единовременно, и именовать её отдельно нет смысла.
Типичные применения: ключ сортировки `sorted(items, key=lambda x: x['price'])`, фильтрация `filter(lambda x: x > 0, values)`, простые callback в map/filter.
Из практики: если логика lambda требует более одного выражения — выношу в именованную функцию. Сложная lambda хуже читается, чем простая def с понятным именем.
Частые ошибки
Выносят сложную логику в lambda, теряя читаемость и возможности отладки.
Злоупотребляют анонимностью вместо явного определения функции, даже когда нужна повторная используемость.
Дополнительные вопросы
Где lambda-функции используются в стандартной библиотеке Python?
Почему некоторые линтеры рекомендуют именованные функции вместо lambda в определённых случаях?
Что такое глубокая и поверхностная копия?
Middle
Поверхностная копия (shallow copy) создаёт новый объект, но вложенные объекты не дублируются — копируются их ссылки. `copy.copy()` или `list[:]`.
Глубокая копия (deep copy) рекурсивно копирует все вложенные объекты, создавая полностью независимую структуру. `copy.deepcopy()`.
Из практики: при передаче тестовых fixture-объектов между тестами использую deepcopy — shallow copy однажды подвела: тест модифицировал вложенный список, ломая данные для следующего теста.
Частые ошибки
Путают копирование ссылки с копированием объекта: присваивание не создаёт ни глубокую, ни поверхностную копию.
Используют shallow copy там, где нужен deep.
Дополнительные вопросы
Как в Python сделать глубокую копию списка, содержащего другие списки?
Почему глубокая копия может быть значительно медленнее и требовательнее к памяти?
Что такое функции высшего порядка?
Middle
Функции высшего порядка — это функции, которые принимают другие функции или возвращают их.
Они полезны, когда нужно переиспользовать общий алгоритм и подставлять разное поведение через колбэки.
Из практики: такие функции часто упрощают фильтрацию и преобразование тестовых данных.
Частые ошибки
Не понимают отличие от обычных функций: функция высшего порядка – это скорее способ использования функций, а не особый синтаксис.
Путают с методами класса высшего порядка (нет такого понятия).
Дополнительные вопросы
Можно ли хранить функцию в переменной в Python?
Пример функции высшего порядка из стандартной библиотеки?
Что такое сложность алгоритмов? Как оценивается? Какая сложность основных операций в коллекциях?
Middle
Big O описывает рост времени/памяти при увеличении входных данных. Основные классы: O(1) — константа, O(log n) — бинарный поиск, O(n) — линейный перебор, O(n log n) — сортировка, O(n²) — вложенные циклы.
Сложность операций в коллекциях Python: list — доступ O(1), поиск O(n), append O(1); dict — доступ/поиск/вставка O(1) амортизированно; set — проверка вхождения O(1); deque — append/pop с обоих концов O(1).
Из практики: понимание сложности помогает выбрать правильную структуру данных и не убить производительность тестов на больших наборах данных.
Частые ошибки
Запоминают сложности наизусть, но не учитывают худшие случаи (например, коллизии в словаре могут дать O(n)).
Считают, что сложность точно предсказывает скорость всегда, игнорируя константные факторы и распределение данных.
Дополнительные вопросы
Какова сложность сортировки и почему?
Почему вставка в начало списка медленная, а в deque быстрее?
Что такое `super` в классе?
Middle
`super()` возвращает прокси-объект для вызова методов родительского класса. Используется внутри переопределённого метода для доступа к реализации родителя.
В Python 3 `super()` без аргументов автоматически определяет текущий класс. При множественном наследовании работает по MRO-цепочке.
Из практики: в базовом классе BasePage вызываю `super().__init__(driver)` — это гарантирует корректную инициализацию всей иерархии классов при множественном наследовании Page Object.
Частые ошибки
Вызывают методы родителя напрямую по имени класса, что ломает MRO, вместо использования super().
Забывают передавать `super()` необходимые аргументы или использовать его в сложном наследовании, вызывая дублирование логики.
Дополнительные вопросы
Как работает MRO (Method Resolution Order) при вызове super()?
Можно ли вызвать метод не прямого родителя через super()?
Что такое декораторы? Для чего они нужны?
Middle
Декоратор — функция или класс, которые оборачивают другую функцию для расширения её функциональности без изменения исходного кода.
Синтаксис `@decorator` эквивалентен `func = decorator(func)`. Используются для: логирования, кэширования (lru_cache), контроля доступа, повторных попыток (retry), аннотаций Allure.
Из практики: пишу декоратор `@retry(times=3, delay=1)` для нестабильных сетевых шагов в API-тестах. Понимание механизма декораторов помогает разбирать фреймворки типа pytest и allure.
Частые ошибки
Путают декоратор и декорируемую функцию: важно возвращать функцию-обёртку.
Забывают сохранять __name__ и __doc__ оригинальной функции (решается через functools.wraps).
Дополнительные вопросы
Как написать декоратор, принимающий параметры?
Могут ли декораторы применяться к классам?
Что такое абстрактные классы? Что такое интерфейсы?
Middle
Абстрактный класс — класс с абстрактными методами, от которого нельзя создать экземпляр напрямую. В Python реализуется через ABC (Abstract Base Class): `from abc import ABC, abstractmethod`; подклассы обязаны реализовать все @abstractmethod.
В Python нет отдельного ключевого слова interface, но его роль выполняют ABC и Protocol (из typing). Protocol описывает структурный контракт (duck typing) — если объект имеет нужные методы, он соответствует Protocol без явного наследования.
Из практики: ABC использую когда нужно общее поведение и принудительный контракт для иерархии; Protocol — когда важна гибкость и не хочу привязывать сторонние классы к наследованию.
Частые ошибки
Считают, что абстрактные классы не могут содержать реализацию (в Python могут, помимо абстрактных методов).
Пытаются напрямую instantiate абстрактный класс.
Дополнительные вопросы
Как сделать метод абстрактным в Python?
Чем отличается абстрактный класс от протокола (Protocol) в typing?
Как в Python реализованы доступы внутри класса? Можно ли получить доступ к приватным атрибутам и методам?
Middle
В Python есть соглашения по видимости: публичные, «защищённые» с одним подчёркиванием и «приватные» с двойным подчёркиванием.
Доступ к «приватным» атрибутам технически возможен через манглинг имени, поэтому это больше защита от случайного использования, чем жёсткий запрет.
Из практики: такие атрибуты трогаю только в крайних случаях, обычно лучше работать через публичный интерфейс класса.
Частые ошибки
Считают, что __ делает атрибут полностью недоступным.
Злоупотребляют доступом к внутренним атрибутам, нарушая инкапсуляцию (например, через obj._ClassName__x).
Дополнительные вопросы
Для чего используется модуль `inspect` в контексте доступа к атрибутам?
Почему в Python решили не вводить строгие приватные члены классов?
Что такое миксины?
Middle
Миксин — класс, предназначенный для «примешивания» поведения в другие классы через множественное наследование. Сам по себе не используется.
Обычно миксины предоставляют конкретные методы (логирование, сериализацию, валидацию) и не имеют собственного состояния (нет `__init__`).
Из практики: создаю `LoggingMixin` с методом `log_request()` и подмешиваю его в API-клиенты — все клиенты получают логирование без копипаста кода. Важно не злоупотреблять: глубокое множественное наследование сложно отлаживать.
Частые ошибки
Путают миксин с обычным классом: миксин не должен иметь состояние, требующее инициализации через __init__.
Создают глубокие и сложные иерархии миксинов, затрудняя поддержку (принцип KISS нарушается).
Дополнительные вопросы
Как в Python указать, что класс предназначен как миксин (не должен существовать сам по себе)?
Приведите пример использования миксинов в стандартной библиотеке?
Что такое асинхронность? Что такое корутина?
Middle
Асинхронность позволяет задачам приостанавливать выполнение и уступать управление другим задачам, не блокируя поток. В Python реализована через asyncio и ключевые слова async/await.
Корутина — функция, объявленная через async def, которая может приостанавливаться в точках await, пока ожидает I/O или другую корутину. Event loop управляет переключением между корутинами.
Из практики: async/await особенно полезен при тестировании API и работе с WebSocket — позволяет запускать несколько запросов конкурентно без многопоточности.
Частые ошибки
Смешивают асинхронность с параллельностью: асинхронные задачи выполняются в одном потоке, чередуя выполнение.
Забывают запускать корутины через event loop.
Дополнительные вопросы
Что такое asyncio и для чего он нужен?
Как «усыпить» корутину (сделать паузу) в Python?
Что такое поток? Что такое процесс?
Middle
Процесс — отдельная единица выполнения со своей памятью, регистрами и файловыми дескрипторами. Создаётся ОС и изолирован: крах одного не ронит другой. Поток (thread) существует внутри процесса, разделяет его память и ресурсы с другими потоками.
В CPython потоки ограничены GIL: реальный параллелизм возможен только при I/O-ожидании. Для CPU-параллелизма нужен multiprocessing. subprocess.Popen создаёт отдельный процесс с изолированной памятью.
Из практики: в pytest-xdist `-n auto` создаёт отдельные процессы, не потоки — именно поэтому обходит GIL и реально параллелит тесты на нескольких ядрах. При потоках такого выигрыша не получить из-за GIL.
Частые ошибки
Путают потоки с процессами: не учитывают, что потоки разделяют память, а процессы изолированы.
Не учитывают, что в CPython многопоточность параллельно CPU неиспользуется из-за GIL (для вычислительных задач).
Дополнительные вопросы
Как потоки и процессы взаимодействуют между собой (IPC, GIL)?
Когда предпочтительнее использовать процессы вместо потоков?
Что такое конкурентность и параллелизм?
Middle
Конкурентность (concurrency) — свойство программы быть способной работать над несколькими задачами одновременно: задачи могут переключаться, прогресс идёт у всех, но не обязательно одновременно. В Python реализуется через threading или asyncio.
Параллелизм (parallelism) — реальное одновременное выполнение нескольких задач в один момент времени, требует нескольких ядер. В Python достигается через multiprocessing, обходящий GIL.
Из практики: в тестировании API concurrency нужна для одновременной отправки запросов (asyncio или threading — достаточно, GIL снимается при I/O). Для параллельного запуска тестовых процессов применяю multiprocessing через pytest-xdist.
Частые ошибки
Считают эти термины синонимами, не различая одновременность выполнения и структурное переплетение задач.
Полностью отвергают ценность конкурентности на одном ядре, хотя она улучшает отзывчивость за счёт асинхронности.
Дополнительные вопросы
Как связаны конкурентность и асинхронность?
Может ли быть параллелизм без конкурентности или наоборот?
Что такое многопоточность? Что такое многопроцессорность?
Middle
Многопоточность (multithreading) — несколько потоков выполнения в одном процессе, разделяющих общую память. В Python: модуль threading. Из-за GIL потоки не дают прироста на CPU-задачах, зато эффективны при I/O (сетевые запросы, файлы).
Многопроцессорность (multiprocessing) — несколько отдельных процессов с изолированной памятью. В Python: модуль multiprocessing. Позволяет обойти GIL и использовать несколько ядер для CPU-интенсивных задач.
Из практики: при нагрузочном тестировании API использую threading для конкурентных запросов — GIL снимается при ожидании сети. Для параллельного прогона тестов использую multiprocessing (pytest-xdist -n auto) — реальный прирост скорости на CI с несколькими ядрами.
Частые ошибки
Терминологически путают «многопроцессорность» с «multiprocessing»: многопроцессорность обычно про аппаратное, а multiprocessing – про запуск процессов.
Не учитывают, что межпроцессное взаимодействие сложнее (надо передавать данные через очереди, пайпы).
Дополнительные вопросы
Почему многопоточность в Python не ускоряет вычисления на CPU?
Как передаются данные между процессами в Python?
Что такое состояние гонки?
Middle
Состояние гонки возникает, когда несколько потоков или процессов одновременно меняют общие данные без синхронизации.
Из-за этого результат может зависеть от случайного порядка выполнения и воспроизводиться нестабильно.
Из практики: такие дефекты лучше всего ловятся повторяемыми стресс-сценариями и подробным журналированием шагов.
Частые ошибки
Сводят только к многопоточности, забывая, что race condition возможен и при межпроцессном взаимодействии или даже с асинхронностью (например, несколько корутин).
Предполагают, что добавление произвольных задержек решает проблему.
Дополнительные вопросы
Как обнаруживать и отлаживать состояния гонки?
Какие стратегии предотвращают race condition?
Что такое примитивы синхронизации? Какие они бывают?
Middle
Примитивы синхронизации координируют доступ нескольких потоков к общим ресурсам. Основные: Lock/RLock (взаимное исключение), Semaphore (ограничение числа одновременных потоков), Event (сигнал между потоками), Condition (ожидание условия).
RLock (реентерабельный) позволяет одному потоку захватить блокировку несколько раз. Barrier синхронизирует группу потоков в точке встречи. Queue из модуля queue — потокобезопасная очередь для передачи данных.
Из практики: в тестировании конкурентных сценариев использую Event для сигнализации между потоками и Queue для безопасной передачи результатов из фоновых задач.
Частые ошибки
Ограничиваются только одним видом.
Неправильно выбирают примитив: например, вместо семафора используют множество Lock’ов, усложняя дизайн.
Дополнительные вопросы
Чем отличается Mutex от Semaphore?
Как реализовать блокировку между процессами (а не потоками) в Python?
Как вы валидируете данные в Python (Pydantic и Marshmallow)?
Middle
Pydantic (v2): определяю `class User(BaseModel)` с аннотированными полями, валидация происходит автоматически при создании объекта. `model_validate(dict)` для парсинга, `model_dump()` для сериализации. `Field(...)` задаёт ограничения.
Marshmallow: схема наследуется от `Schema`, поля объявляются как атрибуты класса. `schema.load(data)` десериализует и валидирует, `schema.dump(obj)` сериализует. Более гибкий для кастомной логики.
Из практики: Pydantic v2 использую для валидации API-ответов в тестах — создаю модель ответа и `ResponseModel.model_validate(response.json())` сразу покажет расхождение с контрактом.
Частые ошибки
Избыточная валидация в тестах.
Неправильное использование Optional типов.
Дополнительные вопросы
Pydantic vs dataclasses - когда что?
Опыт с custom validators?
Как бы вы написали функцию проверки палиндрома?
Junior
Сравниваю строку с её обратной версией после нормализации: приводим к нижнему регистру, удаляем пробелы и знаки препинания. Пример: `s = re.sub(r"[^a-zа-я0-9]", "", s.lower()); return s == s[::-1]`.
Покрываю крайние случаи: пустая строка (True), один символ (True), строка из пробелов/знаков (True после нормализации), строка с цифрами.
Из практики: на живом кодинге важно первым делом уточнить требования — учитывать пробелы и регистр или нет. После написания сразу показываю один-два теста на граничных значениях — это демонстрирует TDD-подход и внимание к краевым случаям.
Частые ошибки
Забывают про нормализацию входных данных.
Дополнительные вопросы
Как протестируешь Unicode-палиндромы?
Как бы вы отсортировали список?
Junior
Уточняю требования: по возрастанию/убыванию, стабильность сортировки, тип данных (числа, строки, объекты). В Python: `sorted(lst)` — новый список, `lst.sort()` — in-place. Для объектов: `sorted(items, key=lambda x: x["price"])`.
Стандартная сортировка Python (Timsort) — стабильная и O(n log n). Для убывания: `sorted(lst, reverse=True)` или `key=lambda x: -x["price"]`.
Из практики: на живом кодинге сначала пишу базовый случай, потом добавляю edge cases в тестах: пустой список, один элемент, список с дублями, список с None. Вопрос «а что делать с None?» — хорошая точка для демонстрации инженерного мышления.
Частые ошибки
Не уточняют условия задачи до начала кода.
Дополнительные вопросы
Как отсортируешь по нескольким полям?
Как бы вы написали простой API-тест?
Junior
Пишу минимально рабочий тест: подготовка данных (если нужна), отправка запроса через requests или httpx, проверка status code и ключевых полей тела ответа. Пример: `response = requests.post(url, json=payload, headers={"Authorization": token}); assert response.status_code == 201; assert response.json()["id"] is not None`.
Добавляю негативный кейс: неверные данные → 400 с понятным сообщением. Без авторизации → 401. Если применимо — проверяю состояние в БД через отдельный GET-запрос.
Из практики: хороший API-тест виден как тест, а не как скрипт. Явные assert с сообщением, понятные имена переменных и строгий teardown — по ним сразу видно уровень кандидата на живом кодинге.
Частые ошибки
Проверяют только статус без содержимого ответа.
Дополнительные вопросы
Какие проверки добавишь вторым шагом?
Как бы вы написали фикстуру?
Junior
Пишу фикстуру с явным setup и teardown через yield: всё до yield — подготовка, после — очистка. Выбираю scope осознанно: `function` для изолированных тестов, `session` для дорогих ресурсов типа токена авторизации или Docker-контейнера.
Важно: teardown должен выполняться даже при падении теста. Паттерн с yield гарантирует это в отличие от addfinalizer.
Из практики: на живом кодинге показываю фикстуру с yield, объясняю разницу scope и демонстрирую autouse=True как отдельный инструмент. Сразу проговариваю: если teardown не чистит данные надёжно — это источник флаки при параллельном запуске.
Частые ошибки
Фикстура делает слишком много и становится хрупкой.
Дополнительные вопросы
Какой scope выберешь для API-клиента и почему?
Что такое фикстура?
Junior
Фикстура в Pytest готовит и при необходимости очищает окружение для теста.
Она уменьшает дублирование в тестах и делает подготовку данных единообразной.
Из практики: правильно оформленные фикстуры заметно повышают стабильность тестового набора.
Частые ошибки
Злоупотребляют фикстурами для тривиальных данных (где проще объявить внутри теста).
Путают фикстуру с тестом: фикстура должна только готовить условия, а не выполнять проверки.
Дополнительные вопросы
Как вернуть значение из фикстуры и использовать его в тесте?
Как реализовать выполнение кода teardown (очистки) в фикстуре?
Что такое scope в фикстуре? Расскажи про каждый уровень подробнее?
Middle
Scope определяет, как часто фикстура создаётся: function (по умолчанию) — новая для каждого теста, полная изоляция; class — одна на все тесты класса; module — одна на весь файл; session — одна на весь прогон.
Более широкий scope ускоряет тесты (меньше создания/удаления), но снижает изоляцию. Фикстура с session-scope должна быть потокобезопасной при параллельном запуске.
Из практики: использую function для данных, class для логически связанных тестов, module/session для тяжёлых ресурсов — БД-соединения, Docker-контейнеры. Неверный scope — самая частая причина флаки-тестов.
Частые ошибки
Задают неоправданно широкий scope (session) для фикстуры, требующей изоляции, из-за чего тесты влияют друг на друга.
Забывают, что фикстура с широким scope не переинициализируется для каждого теста, и сохраняет состояние.
Дополнительные вопросы
Как Pytest определяет порядок инициализации фикстур разного scope?
Можно ли внутри фикстуры с более широким scope звать фикстуру с более узким?
Можно ли сделать так, чтобы фикстура вызывалась автоматически?
Middle
Да, для этого у фикстуры выставляется параметр `autouse=True`. Фикстура применяется ко всем тестам в области видимости (файл или весь проект при размещении в `conftest.py`) без явного указания в параметрах теста.
Подходит для фонового окружения: автоматический сброс данных между тестами, установка заголовков авторизации, логирование каждого шага — всё, что нужно всем тестам без исключения.
Из практики: осторожно с autouse в широком scope — она работает для всех тестов файла или проекта, в том числе там, где не нужна. Предпочитаю явную передачу фикстуры, чтобы зависимости теста были видны в его сигнатуре. autouse применяю только для инфраструктурных вещей типа очистки БД или сессии логирования.
Частые ошибки
Устанавливают autouse для «тяжёлых» фикстур, что замедляет все тесты, даже не нуждающиеся в ней.
Неявный автозапуск усложняет понимание, какие условия действуют на тест.
Дополнительные вопросы
Как ограничить autouse-фикстуру только определёнными тестами (например, по метке)?
Может ли автозапускаемая фикстура иметь функцию teardown, и когда она выполнится?
Какие встроенные фикстуры знаешь?
Middle
Часто используемые встроенные фикстуры: tmp_path — временная директория (Path-объект), уникальная для каждого теста; capfd / capsys — перехват вывода в stdout/stderr; monkeypatch — патчинг атрибутов, переменных окружения, функций.
Также: request — доступ к метаданным теста и параметрам; pytestconfig — доступ к конфигурации запуска; mocker — из плагина pytest-mock, удобная обёртка над unittest.mock.
Из практики: monkeypatch незаменим для тестирования кода, зависящего от env-переменных или системного времени — патчу нужные значения без правки кода.
Частые ошибки
Не знают про существование встроенных фикстур и пишут собственные велосипедные решения.
Злоупотребляют monkeypatch, вмешиваясь в глобальное состояние слишком широко, что может осложнить другие тесты.
Дополнительные вопросы
Как получить доступ к командной строке Pytest внутри теста (например, узнать имя текущего теста)?
Что возвращает фикстура capsys и как ей пользоваться?
Что такое генератор? Как написать его?
Middle
Генератор — это функция, которая возвращает значения по одному через `yield`, а не строит всё сразу в памяти.
Он полезен для больших наборов данных и потоковой обработки, где важна экономия памяти.
Из практики: генераторы часто спасают тесты от лишнего потребления памяти на больших фикстурах.
Частые ошибки
Путают генераторную функцию (с yield) и генераторное выражение: оба создают генератор, но синтаксис разный.
Забывают, что генератор одноразовый.
Дополнительные вопросы
Как остановить генератор преждевременно?
Чем генератор отличается от обычной функции по механизму выполнения?
Что такое GIL? Преимущества и недостатки?
Middle
GIL (Global Interpreter Lock) — мьютекс в CPython, который позволяет исполнять байт-код только одному потоку одновременно. Упрощает управление памятью и делает отдельные операции потокобезопасными без явных блокировок.
Главный недостаток: GIL не позволяет Python-потокам реально параллельно выполнять CPU-интенсивный код на нескольких ядрах. Для CPU-параллелизма нужен multiprocessing. Для I/O-задач GIL практически не мешает, так как снимается при ожидании.
Из практики: при параллельном запуске тестов использую pytest-xdist с multiprocessing (-n auto) — это обходит GIL и реально ускоряет прогон на многоядерных машинах.
Частые ошибки
Думают, что GIL влияет на параллелизм I/O-задач.
Считают, что GIL есть во всех реализациях Python.
Дополнительные вопросы
Почему GIL не убирают из CPython до сих пор?
Какое преимущество даёт GIL при работе с не потокобезопасными C-библиотеками?
Что такое паттерны проектирования и где они полезны в Python?
Middle
Паттерн проектирования — типовое решение часто встречающейся архитектурной задачи. Классифицируются по GoF: порождающие (Builder, Factory, Singleton), структурные (Adapter, Decorator, Proxy), поведенческие (Strategy, Observer, Command).
В Python паттерны часто реализуются проще, чем в Java, за счёт duck typing, декораторов и функций первого класса.
Из практики: в тестовых проектах Builder использую для создания сложных объектов данных, Strategy — для разных стратегий авторизации, Observer — для тестовых слушателей событий в Allure.
Частые ошибки
Зубрят список паттернов без понимания применения.
Считают паттерны догмой: применяют шаблон даже когда проблема в этом не нуждается, усложняя код.
Дополнительные вопросы
Приведите пример применения паттерна Наблюдатель (Observer) в реальной библиотеке?
Какие паттерны менее актуальны в Python из-за динамической природы языка?
Что такое метаклассы в Python и когда они нужны?
Senior
Метакласс управляет тем, как создаются классы в Python.
Нужен редко: обычно для инфраструктурных задач, где надо централизованно проверять или изменять классы при создании.
Из практики: в обычном прикладном коде метаклассы чаще усложняют поддержку, чем помогают.
Частые ошибки
Применяют метаклассы без необходимости, усложняя понимание кода.
Не учитывают, что __init__ метакласса выполняется при импортировании модуля.
Дополнительные вопросы
Как объявить класс с пользовательским метаклассом?
Можно ли объединить использование метаклассов и наследование (какие сложности возникают)?
Что такое diamond problem и MRO в Python?
Senior
Diamond problem — проблема ромбовидного наследования: когда класс D наследует от B и C, оба из которых наследуют от A — возникает неопределённость, какой метод A вызвать.
MRO (Method Resolution Order) — порядок поиска методов. Python использует алгоритм C3-линеаризации: порядок всегда предсказуем и можно проверить через `ClassName.__mro__`.
Из практики: при построении иерархии Page Object с миксинами важно вызывать `super()` в каждом классе цепочки — это гарантирует, что MRO отработает корректно и ни один `__init__` не будет пропущен.
Частые ошибки
Не понимают, как Python разрешает diamond problem: благодаря C3 MRO конфликт решается однозначно.
Пытаются вручную вмешаться в MRO, вместо корректного проектирования и использования super() по MRO.
Дополнительные вопросы
Что произойдёт, если два родителя определяют один метод (как Python решит, какой вызвать)?
Как посмотреть порядок MRO для класса?
Чем кортеж отличается от списка в Python?
Junior
Список (list) — изменяемая упорядоченная коллекция. Кортеж (tuple) — неизменяемая. Поэтому tuple можно использовать как ключ словаря и элемент множества, а list — нет.
Кортежи компактнее по памяти, чуть быстрее на доступ. Также сигнализируют читателю «это фиксированный набор полей», а не «список однотипных значений».
Из практики: возвращайте кортеж, когда функция отдаёт несколько разнотипных значений. Используйте list для коллекции, которая может расти.
Частые ошибки
Создать кортеж из одного элемента без запятой: (5) — это int, а нужно (5,).
Считать tuple полностью неизменяемым — если внутри mutable, его можно менять.
Дополнительные вопросы
Что такое namedtuple?
Когда выбирать tuple vs list?
Что такое list comprehension и какие у него плюсы?
Junior
List comprehension — это компактный синтаксис для создания списка из итерируемого: [x*2 for x in range(10) if x % 2 == 0].
Преимущества: короче и часто быстрее, чем явный цикл с .append(). Бывают аналоги для словарей ({k: v for ...}) и множеств ({x for ...}). Для генераторов — круглые скобки.
Из практики: для одного-двух преобразований comprehension читабелен. Для сложной логики лучше явный цикл или функция.
Частые ошибки
Делать многострочные comprehension с тремя if/for — читается хуже цикла.
Использовать вместо filter()/map() из привычки, теряя выразительность.
Дополнительные вопросы
Чем generator expression отличается от list comprehension?
Когда лучше map/filter?
Что такое GIL в Python?
Middle
GIL (Global Interpreter Lock) — это мьютекс в CPython, позволяющий только одному потоку выполнять Python-байткод в каждый момент. Память управляется без гонок.
Следствие: настоящий многопоточный CPU-bound параллелизм невозможен. Для IO-bound потоки помогают, потому что GIL отпускается на время блокирующих операций.
Из практики: CPU-bound задачи распараллеливают через multiprocessing (отдельные процессы) или asyncio (для IO). Также Cython, NumPy, multiprocessing.Pool обходят GIL по-разному.
Частые ошибки
Ожидать ускорения CPU-bound кода от threading в CPython.
Считать, что GIL — баг. Это компромисс в дизайне.
Дополнительные вопросы
Чем отличается threading от multiprocessing?
Что такое no-GIL Python (PEP 703)?
Чем asyncio отличается от threading?
Middle
asyncio — это однопоточная кооперативная многозадачность через event loop. Корутины (async/await) приостанавливаются в точках await, давая работать другим.
threading — потоки ОС. В CPython упираются в GIL для CPU-bound, но дают параллелизм для IO-bound (через release GIL).
Из практики: для IO-bound (тысячи параллельных HTTP-запросов) asyncio эффективнее. Threading удобен, когда нужно работать с блокирующими библиотеками.
Частые ошибки
Звать блокирующий код внутри async-функции и блокировать event loop.
Смешивать threading и asyncio без understanding interop.
Дополнительные вопросы
Что такое asyncio.to_thread()?
Какие самые частые ошибки в asyncio?
Что такое context manager и зачем with-конструкция?
Junior
Context manager — это объект, реализующий __enter__ и __exit__ (или async-варианты). Используется с with: с гарантией, что __exit__ вызовется, даже при исключении.
Стандартное применение: открытие файлов, соединений с БД, lock'ов, transactions. with open('f') as f: гарантирует закрытие файла.
Из практики: декоратор contextlib.contextmanager упрощает создание собственных менеджеров. Это лучший способ управлять ресурсами в Python.
Частые ошибки
Открывать файлы без with и забывать закрывать.
Делать тяжёлую работу в __exit__ — медленный выход.
Дополнительные вопросы
Как написать свой context manager?
Что такое async with?
Что такое декоратор в Python?
Middle
Декоратор — это функция (или класс), которая принимает функцию и возвращает новую функцию, обычно расширяющую поведение оригинала: логирование, проверка прав, кеширование.
Синтаксис @decorator над определением — это сахар над function = decorator(function). Поддерживает параметры через ещё один уровень вложенности.
Из практики: широко используется в Django, Flask, FastAPI, pytest (фикстуры, параметризация). Свои декораторы пишут для cross-cutting concerns.
Частые ошибки
Забыть functools.wraps — теряются имя и docstring.
Делать декоратор с состоянием — глобальный mutable стейт.
Дополнительные вопросы
Что делает functools.wraps?
Как сделать декоратор с параметрами?
Что такое генератор и yield?
Middle
Генератор — функция, в которой есть yield. При вызове она возвращает iterator-объект. На каждом __next__ выполнение идёт до следующего yield, отдаёт значение, замораживает состояние.
Также есть generator expression — компактный синтаксис: (x*2 for x in range(10)). Не создаёт список целиком, экономит память.
Из практики: незаменимо для обработки больших данных — построчное чтение файлов, потоковая обработка, пайплайны. lazy-семантика снижает потребление памяти.
Частые ошибки
Использовать список вместо генератора для миллиардов элементов — OOM.
Перебирать генератор дважды — он одноразовый.
Дополнительные вопросы
Что такое yield from?
Чем coroutine отличается от generator?
Что такое type hints и зачем они?
Middle
Type hints — аннотации типов: def add(x: int, y: int) -> int. Сам Python не проверяет их в рантайме, но IDE и mypy/pyright дают подсказки и проверку статически.
Сильно повышают читаемость и снижают число багов на больших проектах. Поддерживаются Generic, TypedDict, Protocol, Literal, Union, Optional.
Из практики: для production-кода стоит включить mypy в CI. Полная типизация даёт уверенность при рефакторингах.
Частые ошибки
Думать, что type hints проверяются в рантайме — нужны pydantic, runtime-валидаторы.
Игнорировать предупреждения mypy.
Дополнительные вопросы
Что такое Protocol в Python?
Чем pydantic дополняет type hints?
Что такое pickle и какие опасности?
Middle
pickle — формат сериализации объектов Python в байты. Сохраняет почти любой объект, в т.ч. свои классы.
Опасность: десериализация pickle из недоверенного источника = удалённое выполнение кода. Pickle позволяет вызвать произвольный код при загрузке.
Из практики: используйте pickle только внутри своего процесса для своих данных. Для обмена с внешним миром — JSON, MessagePack, Protobuf.
Частые ошибки
Загружать pickle из пользовательского ввода или сети.
Полагаться на совместимость pickle между версиями Python.
Дополнительные вопросы
Чем dill отличается от pickle?
Какие безопасные альтернативы?
Что такое виртуальное окружение и зачем оно?
Junior
Virtual environment — изолированная директория с собственным Python и своим набором пакетов. Не пересекается с системным Python и другими проектами.
Создают через python -m venv .venv. Активируют source .venv/bin/activate. Все pip install ставят в эту среду.
Из практики: всегда работайте в venv. Альтернативы — poetry, pipenv, uv. Это убирает конфликты версий между проектами и облегчает развёртывание.
Частые ошибки
Ставить пакеты глобально через sudo pip — конфликты с системой.
Хранить .venv в репозитории.
Дополнительные вопросы
Чем poetry лучше venv+pip?
Что такое requirements.txt?
Что такое dunder-методы (магические методы)?
Middle
Методы с двойным подчёркиванием: __init__ (инициализация), __str__ (строковое представление), __eq__ (равенство), __hash__, __iter__, __len__, __add__ и др.
Они определяют, как объект ведёт себя со встроенными операциями (print, ==, in, len, +).
Из практики: реализуя dunder-методы, делают свои классы pythonic — они интегрируются со стандартным языком, как встроенные.
Частые ошибки
Реализовать __eq__ и забыть __hash__ — объект перестанет работать как ключ.
Делать в dunder тяжёлую работу — неожиданные тормоза.
Дополнительные вопросы
Что такое __slots__?
Зачем нужен __repr__ vs __str__?
Что такое MRO и diamond problem?
Middle
MRO (Method Resolution Order) — порядок, в котором Python ищет методы при множественном наследовании. Алгоритм C3 linearization даёт детерминированный порядок.
Diamond problem — когда A наследуется от B и C, оба — от D. Без правильного MRO непонятно, чей метод вызвать. C3 даёт ответ и super() работает корректно.
Из практики: смотрят через ClassName.__mro__ или mro(). Понимание MRO критично, когда строите сложные иерархии с миксинами.
Частые ошибки
Использовать сложную множественную наследственность без понимания MRO.
Не использовать super() и ломать цепочку.
Дополнительные вопросы
Что такое cooperative multiple inheritance?
Как Python разрешает конфликты MRO?
Что такое dataclass и когда его использовать?
Middle
@dataclass — декоратор, генерирующий __init__, __repr__, __eq__ для класса с типизированными полями. Доступен с Python 3.7.
Похоже на data class в Kotlin или record в Java. Поддерживает frozen=True (иммутабельность), default, default_factory.
Из практики: для DTO, конфигов, value-объектов. Резко уменьшает шаблонный код. Для строгой валидации — pydantic, но dataclass быстрее на стандартных задачах.
Частые ошибки
Использовать default=[] — общий список между экземплярами.
Считать frozen=True полной иммутабельностью — внутри изменяемые объекты можно менять.
Дополнительные вопросы
Чем dataclass отличается от pydantic?
Что такое __post_init__?
Что такое requests и какие альтернативы?
Junior
requests — самая популярная библиотека для HTTP-запросов в Python: requests.get, .post, .put и т.д. Простой интерфейс, JSON-парсинг, поддержка сессий.
Из практики: для скриптов и тестов — requests. Для async-приложений (FastAPI и др.) — httpx. requests разрабатывается медленно, но остаётся стандартом.
Частые ошибки
Не задавать timeout — запрос может висеть вечно.
Игнорировать verify=True по умолчанию и отключать без причины.
Дополнительные вопросы
Что такое requests.Session?
Чем httpx удобнее requests?
Что такое pytest и чем он удобнее unittest?
Junior
pytest — самый популярный фреймворк тестирования в Python. Простой синтаксис (обычные функции с assert вместо self.assertEqual), мощные фикстуры, параметризация, плагины.
В сравнении с unittest: меньше boilerplate, лучшие сообщения об ошибках (assert rewriting), огромная экосистема плагинов (pytest-asyncio, pytest-cov, pytest-xdist).
Из практики: для нового проекта почти всегда pytest. unittest остаётся, если уже есть много тестов или нужна совместимость со старыми инструментами.
Частые ошибки
Использовать наследование TestCase и pытаться смешивать стили.
Не пользоваться assert rewriting (использовать assertEqual).
Дополнительные вопросы
Что такое assert rewriting?
Какие плагины обязательны?
Что такое фикстура в pytest?
Junior
Фикстура — функция с @pytest.fixture, готовящая ресурсы и/или данные для тестов. Тест получает её как аргумент по имени.
Поддерживает scope (function, class, module, session) — как часто пересоздавать. Также есть autouse и параметризованные фикстуры.
Из практики: фикстуры в conftest.py доступны всем тестам в директории. Удобно для общих setup'ов: подключение к БД, тестовый клиент, авторизованный пользователь.
Частые ошибки
Делать тяжёлую фикстуру с scope=function — медленно.
Не использовать teardown (yield + cleanup) — мусор после тестов.
Дополнительные вопросы
Что такое yield-фикстура?
Чем conftest.py удобен?
Как параметризовать тест в pytest?
Junior
@pytest.mark.parametrize('input,expected', [(1, 2), (3, 6), (4, 8)]) — тест выполнится трижды с разными значениями. Имена параметров — аргументы функции.
Можно использовать ids=[...] для понятных имён в отчёте. Параметризовать можно и фикстуры через params=[...].
Из практики: незаменимо для data-driven тестов: одна логика, много кейсов. Куда лучше, чем копировать тесты.
Частые ошибки
Слишком много параметров — отчёт нечитаемый.
Параметризовать с большим объектом, который тяжело сериализовать.
Дополнительные вопросы
Что такое indirect параметризация?
Как комбинировать несколько parametrize?
Что такое маркеры в pytest?
Middle
Маркер — это тег на тесте через @pytest.mark.<name>. Встроенные: skip, skipif, xfail, parametrize. Свои — для категоризации (smoke, slow, regression).
Запуск по маркеру: pytest -m smoke. Можно комбинировать: -m 'smoke and not slow'.
Из практики: помогает разделять наборы — smoke в PR, регрессия — в ночном прогоне. Регистрировать кастомные маркеры в pyproject.toml/pytest.ini, иначе будут warning.
Частые ошибки
Не регистрировать кастомные маркеры — warning'и.
Маркировать всё подряд — теряется смысл.
Дополнительные вопросы
Что такое xfail?
Чем skip отличается от skipif?
Что такое conftest.py?
Middle
conftest.py — специальный файл, содержащий общие фикстуры, хуки, плагины для тестов в его директории и поддиректориях.
Pytest ищет conftest.py автоматически. Можно иметь несколько на разных уровнях — пересечение фикстур разрешается по близости.
Из практики: в нём — фикстуры clients, моки, общие хуки (pytest_runtest_setup и др.). Не нужно импортировать — pytest подхватит.
Частые ошибки
Класть всё в один огромный conftest.py — становится непонятно.
Импортировать conftest вручную.
Дополнительные вопросы
Можно ли иметь conftest.py в подкаталогах?
Какие хуки самые полезные?
Что такое pytest-xdist и для чего?
Middle
Плагин pytest-xdist распараллеливает тесты на несколько процессов: pytest -n 4 — 4 параллельно. Также поддерживает запуск на нескольких машинах.
Тесты должны быть изолированными — иначе гонки в общих данных/фикстурах.
Из практики: даёт почти линейное ускорение, если тесты независимы. Распределение — по тестам, по группам, по модулям.
Частые ошибки
Параллелить тесты, делящие БД, без префиксов — гонки.
Использовать -n с большим числом на слабой машине — overhead.
Дополнительные вопросы
Что такое --dist=loadfile?
Как отлаживать flaky-тест после xdist?
Что такое pytest-asyncio?
Middle
Плагин для тестирования asyncio-кода. С помощью @pytest.mark.asyncio (или mode=auto) можно писать async def test_...().
Также поддерживает асинхронные фикстуры. Хорошо сочетается с httpx, aiohttp, FastAPI.
Из практики: для проектов с asyncio плагин обязателен. Версии не всегда обратно совместимы — фиксируйте в зависимостях.
Частые ошибки
Делать длинные async-тесты без таймаутов — могут зависнуть.
Можно задавать порог: --cov-fail-under=80 — упасть, если покрытие ниже 80%.
Из практики: покрытие — индикатор, а не цель. 100% не значит «всё проверено», 60% не значит «всё плохо». Полезно как ограждение от регрессии.
Частые ошибки
Гнаться за процентом покрытия любой ценой — пишут пустые тесты.
Не различать line и branch coverage.
Дополнительные вопросы
Чем branch coverage отличается от line?
Что такое coverage exclusion?
Что такое monkeypatch и fixture mocker?
Middle
monkeypatch — встроенная фикстура pytest для подмены: атрибутов, переменных окружения, времени. Все изменения откатываются автоматически после теста.
pytest-mock даёт фикстуру mocker — обёртку над unittest.mock.patch, тоже с автоочисткой после теста.
Из практики: для подмены глобальных штук, чистых функций — monkeypatch. Для проверки вызовов (assert_called_with) — mocker (через MagicMock).
Частые ошибки
Делать moneypatch без понимания scope — изменения могут просочиться.
Молча моделировать поведение и не проверять, что нужный вызов произошёл.
Дополнительные вопросы
Чем отличается monkeypatch.setattr от mocker.patch?
Как протестировать, что функция вызвана с правильными аргументами?
Какие хуки pytest самые полезные?
Middle
pytest_runtest_setup/teardown — до/после каждого теста. pytest_collection_modifyitems — изменить набор тестов (фильтр, переупорядочить). pytest_addoption — добавить свои CLI-флаги. pytest_html_results_table_row — кастомизировать HTML-отчёт.
Хуки регистрируются в conftest.py.
Из практики: pytest_addoption + фикстура — типовой способ добавить параметры окружения (--env, --browser). modifyitems — для умного skip.
Частые ошибки
Делать тяжёлую работу в каждом хуке — замедление.
Не задокументировать кастомные хуки — никто не понимает поведение.
Дополнительные вопросы
Что такое pytest_terminal_summary?
Как добавить свой CLI-флаг?
Что такое walrus operator (:=)?
Middle
Оператор := (Python 3.8+) присваивает значение в выражении. Удобно в while/if: while (chunk := file.read(1024)): process(chunk).
Избавляет от дублирования вычисления переменной.
Из практики: полезно для list comprehension с условием, обработки потоков, чтения данных частями.
Частые ошибки
Злоупотреблять — снижает читаемость.
Использовать там, где обычный код яснее.
Дополнительные вопросы
Когда walrus операторов уместен?
Какие ограничения по версии Python?
Что такое pathlib и чем она лучше os.path?
Middle
pathlib предоставляет объектно-ориентированный API для путей: Path('foo') / 'bar' дает Path('foo/bar'). Методы read_text, write_text, glob, mkdir.
По сравнению с os.path — куда проще и кросс-платформенно, без ручной работы со строками.
Из практики: для нового кода — pathlib. os.path остаётся в legacy и в местах с особой производительностью.
Частые ошибки
Смешивать pathlib и os.path в одном куске кода без преобразования.
Не использовать as_posix() для совместимых путей.
Дополнительные вопросы
Что такое PurePath?
Чем Path.glob полезен?
Что такое f-strings и их особенности?
Junior
f-string — синтаксис для интерполяции: f"Hello, {name}!". Поддерживает выражения, форматирование, методы.
Быстрее, чем % и .format(). С Python 3.8 — также f"{value=}" для отладки (выводит имя и значение).
Из практики: для нового кода почти всегда f-strings. Кратко, читаемо, эффективно.
Частые ошибки
Использовать f-string с пользовательским вводом в SQL/HTML — инъекция.
Полагать, что f-string безопасен для шаблонов с пользовательским кодом.
Дополнительные вопросы
Что делает f"{value=}"?
Чем f-string быстрее .format?
Что такое @property декоратор?
Middle
@property превращает метод класса в атрибут — обращение без скобок. Также есть .setter и .deleter для записи/удаления.
Удобно для вычисляемых свойств с возможной валидацией: temperature_in_fahrenheit зависит от celsius.
Из практики: используют для инкапсуляции — пользователь работает как с полем, реализация может быть вычислением. Не делайте тяжёлые вычисления в property без явных намёков.
Частые ошибки
Делать тяжёлую работу в property — surprising для пользователя.
Использовать property для случайных эффектов.
Дополнительные вопросы
Чем @property отличается от obj.method()?
Что такое @cached_property?
Что такое magic method __init__ vs __new__?
Middle
__new__ создаёт экземпляр (вызывается до __init__) и возвращает его. __init__ инициализирует уже созданный объект.
Обычно переопределяют только __init__. __new__ — когда нужно контролировать создание (singleton, кэширование, immutable классы).
Из практики: для большинства задач хватает __init__. __new__ — продвинутая тема, и часто признак переусложнения.
Частые ошибки
Перепутать __new__ и __init__.
Делать singleton через __new__ — обычно есть проще решения.
Дополнительные вопросы
Зачем __new__ нужен для immutable?
Можно ли вернуть из __new__ объект другого класса?
Что такое global vs nonlocal?
Middle
По умолчанию присваивание в функции создаёт локальную переменную. global делает её ссылкой на модульную переменную. nonlocal — на переменную в окружающей функции (для замыканий).
Без global/nonlocal присваивание создаст новую локальную, скрывая внешнюю.
Из практики: использование global — обычно признак плохого дизайна. nonlocal иногда нужен в замыканиях. Чаще лучше передавать значения через параметры.
Частые ошибки
Использовать global для shared state в больших проектах.
Делать рекурсивную функцию с global counter.
Дополнительные вопросы
Чем global отличается от просто использования переменной?
print не имеет уровней, форматирования, и направляется только в stdout. В продакшене не контролируется.
Из практики: для серьёзного приложения настраивайте logging с самого начала. JSON-формат для structured logging, чтобы потом удобно искать в Elasticsearch/Loki.
Частые ошибки
Использовать print в production-коде.
Не настраивать уровень и сыпать DEBUG в проде.
Дополнительные вопросы
Что такое structured logging?
Чем loguru удобнее стандартного logging?
Что такое requirements.txt и Poetry?
Middle
requirements.txt — простой файл со списком пакетов и версий. pip install -r установит. Не разделяет dev и runtime, нет lock-файла.
Poetry — современный менеджер: pyproject.toml + poetry.lock, разделение групп зависимостей, виртуальное окружение, публикация. uv — ещё новее и быстрее.
Из практики: для новых проектов Poetry или uv. requirements.txt — для простых скриптов и обратной совместимости.
Частые ошибки
Не фиксировать версии в requirements.txt и получить разные сборки.
Коммитить разные lock-файлы из разных машин без проверки.
Дополнительные вопросы
Что такое pip-tools?
Чем uv быстрее pip?
Что такое match statement (Python 3.10+)?
Middle
match — pattern matching: match value: case 1: ...; case [a, b]: ...; case Point(x=0): .... Гораздо мощнее обычного switch — деструктуризация, охрана условиями, типы.
В отличие от if/elif цепочки, match читается яснее и компилятор может проверить exhaustiveness в типизированном коде.
Из практики: для разбора ADT-подобных структур (Result, Either) match — идеальный инструмент.
Частые ошибки
Использовать match для простого 'if x == 1' — оверкилл.
Не помнить, что простой capture (case x:) ловит всё подряд.
Дополнительные вопросы
Что такое guard в match?
Чем match отличается от if/elif?
Что такое __slots__?
Middle
__slots__ = ('x', 'y') — список разрешённых атрибутов класса. Без него Python хранит атрибуты в __dict__ (более гибко, но больше памяти).
Преимущества: меньше памяти, быстрее доступ, нельзя добавить новый атрибут случайно.
Из практики: применяют для классов, которых много (миллионы экземпляров). Для обычных классов overhead не критичен.
Частые ошибки
Использовать __slots__ везде без необходимости — теряется гибкость.
Не учитывать, что наследование от класса без __slots__ обнуляет выгоду.
Дополнительные вопросы
Чем __slots__ влияет на pickle?
Совместимо ли с dataclass?
Что такое pytest.ini / pyproject.toml для pytest?
Junior
Конфигурация pytest хранится в pyproject.toml ([tool.pytest.ini_options]), pytest.ini или setup.cfg. Указывают: testpaths, python_files, markers, фильтр warnings, опции.
Также флаги по умолчанию через addopts: --strict-markers --tb=short.
Из практики: важно настроить markers, чтобы избавиться от warning. И testpaths, чтобы pytest искал тесты только в нужных директориях.
Частые ошибки
Не настраивать testpaths и собирать все py-файлы как тесты.
Не регистрировать кастомные markers.
Дополнительные вопросы
Что такое addopts?
Где лучше: pytest.ini или pyproject.toml?
Что такое xfail и когда его использовать?
Middle
@pytest.mark.xfail помечает тест как «ожидаемо падающий». Если он падает — тест зелёный (xfailed). Если внезапно проходит — XPASS (это сигнал что-то изменилось).
Часто применяют для известных багов до их фикса — тест отмечает регрессию, но не валит CI.
Из практики: xfail полезен временно. Долго существующий xfail — признак, что баг забыт.
Частые ошибки
Использовать xfail вместо skip для не-багов.
Накапливать xfail без отслеживания.
Дополнительные вопросы
Что такое strict=True для xfail?
Чем xfail отличается от skip?
Что такое pytest plugins и какие популярные?
Middle
Плагины — расширения pytest. Самые ходовые: pytest-asyncio, pytest-cov (покрытие), pytest-xdist (параллелизация), pytest-mock, pytest-django, pytest-html, pytest-bdd.
Подключаются установкой пакета — pytest автоматически их подхватывает.
Из практики: для каждого проекта набор плагинов формируется быстро. Стоит выбирать активно поддерживаемые.
Частые ошибки
Подключать неподдерживаемые плагины — конфликты при обновлении pytest.
Не закреплять версии плагинов.
Дополнительные вопросы
Что такое pytest entry point?
Как написать свой плагин?
Что такое pytest.raises?
Junior
with pytest.raises(ValueError): code_that_should_raise() — проверяет, что код бросает исключение указанного типа.
Можно проверить сообщение: pytest.raises(ValueError, match="invalid").
Из практики: каноничный способ проверить, что исключение брошено. Если код не бросил — тест упадёт.
Частые ошибки
Использовать try/except + assert False вместо pytest.raises.
Не проверять match — ловите любой ValueError.
Дополнительные вопросы
Как проверить, что исключение НЕ брошено?
Что такое pytest.warns?
Что такое captured stdout/stderr в pytest?
Middle
По умолчанию pytest захватывает stdout/stderr тестов и показывает только при падении. Фикстура capsys даёт доступ к захваченному выводу.
Опции: -s отключает захват (всё печатается в реальном времени), --capture=no — то же.
Из практики: capsys полезен для тестирования CLI-инструментов и логирования. -s — для отладки.
Частые ошибки
Полагать, что print в тесте никогда не виден.
Не учитывать, что captured output накапливается.
Дополнительные вопросы
Что такое capsysbinary?
Как протестировать вывод в stderr?
Что такое tmp_path фикстура?
Middle
tmp_path — встроенная фикстура pytest, возвращающая уникальный Path для каждого теста. Автоматически очищается.
Удобно для тестов, работающих с файлами: создаёте файлы в tmp_path, проверяете, не беспокоитесь о cleanup.
Из практики: всегда используйте tmp_path вместо вручную создаваемых /tmp/test_X — изоляция и автоматическая чистка.
Частые ошибки
Хардкодить /tmp пути.
Полагать, что tmp_path удалится сразу после теста (он живёт N последних запусков).
Дополнительные вопросы
Чем tmp_path отличается от tmpdir?
Что такое tmp_path_factory?
Что такое pytest.mark.skipif?
Junior
@pytest.mark.skipif(condition, reason='...') пропускает тест при истинном условии. Часто используют для платформо-зависимых: skipif(sys.platform == 'win32', reason='Unix only').
Также есть встроенные: @skipif(not os.getenv('DB_URL'), reason='no db').
Из практики: типовое применение — тесты, требующие специфичных ресурсов или версий.
Частые ошибки
Использовать skipif везде вместо параметризации.
Не указывать reason.
Дополнительные вопросы
Чем skipif отличается от обычного skip?
Можно ли использовать skipif на классе?
Что такое factory fixture?
Middle
Фикстура, возвращающая функцию-фабрику, а не сам объект. Тест вызывает её несколько раз с разными параметрами: user1 = make_user(name='A'), user2 = make_user(name='B').
Часто комбинируется с подсчётом и очисткой созданных сущностей.
Из практики: незаменимо, когда в тесте нужно несколько похожих объектов с вариациями.
Частые ошибки
Делать factory без cleanup — мусор в БД.
Не использовать factory и плодить дубль кода в тестах.
Дополнительные вопросы
Что такое factory_boy?
Как сделать factory с автоматическим cleanup?
Что такое pytest-bdd?
Middle
Плагин для BDD-стиля: feature-файлы в Gherkin (Given/When/Then), step-определения в Python. Запускается через обычный pytest.
Альтернатива behave с такой же идеей, но интегрированная с pytest и его экосистемой.
Из практики: для команд, где QA пишет сценарии в Gherkin, а разработчики — степы. Минусы: дополнительный слой абстракции, не всегда оправдан.
Частые ошибки
Использовать BDD ради красоты — лишний шум.
Полагать, что BDD автоматически делает тесты бизнес-понятными.
Дополнительные вопросы
Чем pytest-bdd отличается от behave?
Когда BDD оправдан?
Что такое pytest-django?
Middle
Плагин для тестирования Django-приложений. Даёт фикстуры db (общая БД), client (Django test client), live_server, settings и т.д.
Также интегрирует pytest с Django: миграции, transactions, фабрики.
Из практики: для Django-проектов — почти стандарт. Заменяет встроенный django.test и даёт удобства pytest.
Частые ошибки
Использовать django.test.TestCase напрямую — теряется pytest-фикстуры.