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

Что спрашивают у стажёров и джунов на Python: от базовых типов до GIL и декораторов, плюс четыре задачи, которые часто дают на live coding.

Базовые вопросы

  1. Чем list отличается от tuple?

    list изменяемый, tuple — нет. Поэтому кортеж можно использовать как ключ словаря и элемент множества (если внутри тоже неизменяемые объекты), он чуть легче по памяти и подходит для фиксированных наборов, например координат.

  2. Почему поиск по ключу в dict в среднем O(1)?

    Словарь — хеш-таблица: по hash(key) сразу вычисляется, где лежит значение. Поэтому ключ должен быть хешируемым. В худшем случае, при множестве коллизий, поиск деградирует, но на практике это редкость.

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

    == сравнивает значения (вызывает __eq__), is — что это один и тот же объект в памяти. is используют для сравнения с None: if x is None.

  4. Что не так с изменяемым значением по умолчанию: def f(items=[])?

    Значение по умолчанию создаётся один раз при определении функции, и все вызовы делят один и тот же список. Правильно: items=None, а внутри if items is None: items = [].

  5. Поверхностная и глубокая копия — в чём разница?

    copy.copy копирует только внешний объект, вложенные списки и словари остаются общими. copy.deepcopy рекурсивно копирует всё. Срез a[:] — тоже поверхностная копия.

  6. Что такое *args и **kwargs?

    *args собирает лишние позиционные аргументы в кортеж, **kwargs — именованные в словарь. При вызове те же звёздочки распаковывают последовательность и словарь в аргументы.

Вопросы посложнее

  1. Что такое генератор и зачем нужен yield?

    Функция с yield возвращает генератор: значения выдаются по одному и вычисляются лениво. Это экономит память на больших данных — например, при чтении файла построчно. Генератор можно пройти только один раз.

  2. Чем list comprehension отличается от генераторного выражения?

    [x*x for x in data] сразу строит весь список в памяти, (x*x for x in data) отдаёт элементы по требованию. Для sum(), any() и больших данных генератор выгоднее.

  3. Что такое декоратор?

    Функция, которая принимает функцию и возвращает новую с дополнительным поведением: логирование, кэш, проверка прав, повтор при ошибке. @decorator над def — это f = decorator(f). Внутри принято использовать functools.wraps, чтобы сохранить имя и docstring.

  4. Как работает контекстный менеджер with?

    Объект с методами __enter__ и __exit__: ресурс открывается при входе и гарантированно закрывается при выходе, даже при исключении. Примеры — файлы, блокировки, транзакции. Свой можно написать через contextlib.contextmanager.

  5. Что такое GIL и как он влияет на потоки?

    Global Interpreter Lock в CPython позволяет выполнять байткод только одному потоку за раз. Поэтому потоки не ускоряют вычисления на CPU, но хорошо подходят для ожидания ввода-вывода. Для CPU-задач берут multiprocessing, для множества сетевых запросов — asyncio.

  6. Чем @staticmethod отличается от @classmethod?

    classmethod получает класс первым аргументом (cls) и подходит для альтернативных конструкторов. staticmethod не получает ни класса, ни экземпляра — это обычная функция в пространстве имён класса.

  7. Как устроены try / except / else / finally?

    else выполняется, если исключения не было, finally — всегда, даже после return. Ловить лучше конкретные исключения, а не голый except:.

  8. Что такое MRO?

    Method Resolution Order — порядок поиска методов при множественном наследовании (алгоритм C3). Посмотреть можно через Class.__mro__; super() вызывает следующий класс именно по этому порядку.

Задачи на live coding

  1. Найти два числа в списке с заданной суммой

    Один проход со словарём «число → индекс»: для каждого x проверяем, есть ли target - x среди уже встреченных. O(n) по времени вместо O(n²) у перебора.

  2. Проверить, является ли строка палиндромом

    Нормализовать (нижний регистр, только буквы и цифры) и сравнить с разворотом s[::-1] или идти двумя указателями с концов. Спросите про пробелы и регистр до решения.

  3. Посчитать частоту слов и вывести топ-3

    collections.Counter(words).most_common(3). Если просят без библиотек — словарь и сортировка по значению. Не забудьте про регистр и знаки препинания.

  4. Убрать дубликаты, сохранив порядок

    list(dict.fromkeys(items)) — словари в Python 3.7+ сохраняют порядок вставки. Через set порядок теряется.

На задаче сначала проговорите простое решение и его сложность, потом ускоряйте. Подробный план и типовые приёмы — в материале про live coding на собеседовании.