Назад к материалам

Видео + статья / Собеседования

Собеседование Python Backend в Wildberries: GIL, asyncio и Kafka

Правильные ответы на вопросы Python-собеседования в Wildberries: GIL, потоки, asyncio, процессы, декораторы, блокирующий код и Kafka.

Компания
Wildberries
Опубликовано
Обновлено
Видео вышло
Видео
14:26
Текст
9 минут

Если ролик не загружается, выберите другую площадку.

Смотреть на

На техническом собеседовании Python Backend-разработчика в Wildberries кандидату задают шесть плотных вопросов: от GIL и event loop до межпроцессного обмена и Kafka. Здесь особенно хорошо видно, почему знакомого термина недостаточно. Интервьюер ждёт механизм, границы применения и небольшой рабочий пример.

Что такое GIL и как он связан с потоками?

Короткий ответ: в стандартной сборке CPython поток должен владеть Global Interpreter Lock, чтобы работать с Python-объектами. Поэтому два потока обычно не исполняют Python-байткод одновременно.

Это не означает, что многопоточность «не работает». Потоки создаёт операционная система, и она переключает их вытесняюще. Во время блокирующего I/O интерпретатор освобождает GIL, поэтому другой поток может продолжить работу. Документация Python прямо приводит чтение и запись файлов как пример такого освобождения.

Для множества синхронных сетевых вызовов потоки подходят. Для тяжёлых вычислений на Python чаще используют процессы. В актуальных версиях существуют free-threaded сборки без GIL, поэтому на собеседовании лучше уточнять реализацию и версию, а не произносить абсолютное правило.

Как event loop выполняет корутины?

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

Это кооперативное переключение: корутина сама должна дать другим возможность работать. В руководстве по asyncio указано, что пока Task выполняется, другая задача в том же потоке не запускается. Поэтому time.sleep(), синхронный HTTP-клиент или долгий расчёт внутри async def блокируют весь event loop.

Пример кодаPython
import asyncio


async def load_user(user_id: int) -> dict:
    # Асинхронное ожидание отдаёт управление другим задачам.
    await asyncio.sleep(0.1)
    return {"id": user_id}


async def main() -> None:
    # Обе корутины ожидают I/O конкурентно в одном event loop.
    users = await asyncio.gather(load_user(1), load_user(2))
    print(users)


asyncio.run(main())

Как процессы передают данные друг другу?

Короткий ответ: у процессов отдельные адресные пространства, поэтому обычный Python-объект не становится общим автоматически.

multiprocessing.Queue сериализует объекты через pickle и передаёт их между процессами. Manager держит объекты в отдельном серверном процессе и выдаёт proxy — это гибко, но медленнее. Value, Array и shared_memory дают общий участок памяти, для составных изменений всё равно нужна синхронизация. Эти различия подробно описаны в модуле multiprocessing.

Сильный ответ начинается с задачи: поток заданий — Queue, небольшой общий счётчик — Value с блокировкой, крупный массив без лишнего копирования — shared memory.

Чем декоратор отличается от замыкания?

Короткий ответ: декоратор принимает функцию или класс и возвращает преобразованный объект. Замыкание — функция, которая сохраняет значения из окружающей области видимости.

Декоратор часто реализуют через замыкание: внутренняя функция помнит исходную функцию и параметры. Но понятия не равны. Замыкание можно использовать без @decorator, а декоратором может быть объект класса с методом __call__. На практике важно упомянуть functools.wraps, чтобы сохранить имя, документацию и метаданные обёрнутой функции.

Почему синхронный Python-сервер перестаёт отвечать?

Короткий ответ: один долгий вызов занимает worker. Когда заняты все доступные workers, новые запросы ждут очередь.

Один блокирующий запрос не обязан остановить весь сервер: всё зависит от числа процессов или потоков и модели запуска. Для сетевого I/O подходят async-клиент или пул потоков; CPU-задачу выносят в процесс или фоновый worker; долгую бизнес-операцию защищают таймаутом и идемпотентностью. Сначала измеряют, где тратится время, и только затем меняют модель конкурентности.

Как устроены топики, партиции и offsets в Kafka?

Короткий ответ: producer пишет событие в topic, topic разбит на partitions, а offset задаёт позицию записи внутри конкретной партиции.

Порядок Kafka гарантирует внутри одной topic-partition, а не между всеми партициями. События с одинаковым ключом можно направлять в одну партицию, чтобы сохранить их последовательность. Внутри consumer group каждая партиция назначается одному consumer этой группы; другая группа читает тот же поток независимо. Базовая модель собрана во введении Apache Kafka.

Сравнивать Kafka с RabbitMQ только по удалению сообщения неверно. В Kafka записи хранятся по политике retention, а группа хранит свою позицию чтения. Для надёжной обработки дополнительно обсуждают commit offset, повторную доставку и идемпотентность consumer.

Получил ли кандидат оффер?

Факт: решение Wildberries в записи не прозвучало. Поэтому однозначное «да» или «нет» было бы выдумкой.

Наша оценка: для сильной Python Backend-роли кандидат отвечал слишком поверхностно. Он смешал кооперативность asyncio с работой потоков, не раскрыл способы межпроцессного обмена и дал неполную модель Kafka. Вероятнее всего, этого было бы недостаточно для положительного решения на данном техническом этапе.

Исправление понятное: по каждой теме собрать ответ из четырёх частей и выдержать два уточнения. Для этого можно использовать тренажёр собеседований ЮНИКОД, а затем записать собственный ответ и убрать места, где термин не подкреплён механизмом.

Источники

частые вопросы

Короткие ответы

Могут ли потоки Python выполняться параллельно?

В обычной сборке CPython один поток за раз исполняет Python-байткод под GIL. Однако потоки остаются полезны для I/O, нативный код может освобождать GIL, а free-threaded сборки CPython работают иначе.

Почему time.sleep блокирует asyncio, а asyncio.sleep — нет?

time.sleep удерживает поток event loop и не даёт ему запустить другую задачу. asyncio.sleep возвращает управление циклу, который продолжает выполнять готовые корутины.

Получил ли кандидат оффер в Wildberries?

В записи нет решения компании. По качеству технических ответов мы считаем прохождение сильного backend-этапа маловероятным, но это редакционная оценка, а не подтверждённый результат.

следующий шаг

Проверьте ответы до собеседования

Тренажёр ЮНИКОД помогает повторить Python, проговорить механизм своими словами и подготовиться к уточняющим вопросам.

Начать тренировку →