Собеседование по Python: вопросы с ответами и три задачи для практики

Собеседование по Python: вопросы с ответами и три задачи для практики
Содержание статьи

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

На Mentoring.digital собрали учебную подборку для подготовки к техническому интервью. Она не воспроизводит экзамен конкретной компании: набор тем и глубину вопросов нужно сверять с вакансией. Примеры ниже относятся к Python 3.

Как пользоваться вопросами при подготовке junior

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

  • Для вакансии с веб-разработкой дополнительно повторите HTTP, используемый фреймворк и работу с базой данных.
  • Для автоматизации подготовьте пример обработки файлов, неверных данных и ошибок внешнего сервиса.
  • Если нужен разбор пробелов, на индивидуальном курсе Python можно согласовать план подготовки под свой уровень и задачи.

Коллекции, ссылки и изменяемость: четыре вопроса

1. Чем список отличается от кортежа?

Список можно изменять: добавлять элементы, удалять их и заменять. Кортеж не позволяет менять свои элементы, но может содержать изменяемые объекты. Например, список внутри кортежа остаётся списком. Поэтому ответ «внутри кортежа ничего никогда не меняется» неверен.

pair = ([1], "tag")
pair[0].append(2)
print(pair)  # ([1, 2], 'tag')

2. Что сравнивают == и is?

== проверяет равенство значений по правилам типа, а is проверяет, один ли это объект. Два отдельно созданных списка с одинаковыми элементами равны, но не идентичны. Для проверки отсутствующего значения используйте value is None; не проверяйте идентичность чисел и строк вместо равенства.

3. Создаёт ли присваивание копию?

Нет. После b = a обе переменные указывают на один объект. Поверхностная копия создаёт новый внешний контейнер, но сохраняет ссылки на вложенные объекты. Проверьте это на списке списков: изменение вложенного списка будет видно через обе копии. Глубокое копирование решает другую задачу и не должно добавляться автоматически к любому присваиванию.

4. Любой ли кортеж подходит для ключа словаря?

Ключ должен быть хешируемым. Кортеж из чисел подходит, а кортеж со списком внутри не подходит. Не заменяйте это правило фразой «ключом бывает только строка»: числа и другие хешируемые объекты тоже допустимы.

Функции, генераторы и ошибки: ещё четыре вопроса

5. Почему аргумент со списком по умолчанию сохраняет данные?

Значение по умолчанию создаётся при определении функции. Несколько вызовов могут менять один список. Если нужен новый контейнер при каждом вызове, его создают внутри функции. Подробный пример и проверки вынесены в разбор изменяемого аргумента по умолчанию.

6. Чем генератор отличается от готового списка?

Генератор выдаёт значения при последовательном обходе и сохраняет состояние между шагами. После исчерпания того же генератора повторный обход не начнётся заново. Это позволяет не собирать весь результат заранее, но само по себе не гарантирует малое потребление памяти: важны удерживаемые объекты и код генератора.

7. Чем sorted отличается от list.sort?

sorted возвращает новый отсортированный список и принимает итерируемый объект. Метод list.sort меняет сам список и возвращает None. Поэтому выражение result = items.sort() не записывает отсортированные элементы в result.

8. Зачем указывать конкретное исключение?

Если ожидается ошибка преобразования строки в число, обработайте ValueError. Перехват любого исключения с молчаливым продолжением способен скрыть ошибку самого кода. В ответе на интервью объясните, какую ситуацию восстанавливает обработчик и что программа делает дальше.

Три задачи по Python с решениями

Задача 1. Посчитать оплаченные суммы по клиентам

Дан список записей с полями customer, amount и status. Сложите суммы только записей со статусом paid. В этой учебной модели сумма выражена целым числом условных единиц, все обязательные поля присутствуют. Клиента без оплаченных записей в результате быть не должно.

def totals_by_customer(rows):
    totals = {}
    for row in rows:
        if row["status"] != "paid":
            continue
        customer = row["customer"]
        totals[customer] = totals.get(customer, 0) + row["amount"]
    return totals

rows = [
    {"customer": "A", "amount": 120, "status": "paid"},
    {"customer": "A", "amount": 30, "status": "paid"},
    {"customer": "B", "amount": 80, "status": "cancelled"},
]
print(totals_by_customer(rows))  # {'A': 150}

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

Задача 2. Найти второе различное максимальное число

Получите второе по величине различное значение в последовательности целых чисел. Если различных чисел меньше двух, верните None. Повторение максимума не считается вторым максимумом.

def second_largest(values):
    first = second = None
    for value in values:
        if first is None or value > first:
            first, second = value, first
        elif value != first and (second is None or value > second):
            second = value
    return second

print(second_largest([8, 3, 8, 5]))  # 5
print(second_largest([-4, -2, -9]))  # -4
print(second_largest([7, 7]))       # None

Алгоритм хранит два лучших различных значения и проходит данные один раз. Для обычной оценки операций это O(n) по времени и O(1) дополнительной памяти. Нельзя инициализировать оба максимума нулём: такой вариант даст неверный результат для отрицательных чисел.

Задача 3. Разбить последовательность на порции

Напишите генератор порций размера size. Последняя порция может быть короче. Здесь входом служит последовательность со срезами и длиной, например список; произвольный итератор контрактом не предусмотрен. Размер должен быть положительным целым числом.

def batches(sequence, size):
    if size <= 0:
        raise ValueError("size must be positive")
    for start in range(0, len(sequence), size):
        yield sequence[start:start + size]

print(list(batches([1, 2, 3, 4, 5], 2)))
# [[1, 2], [3, 4], [5]]

Пустой список не выдаёт порций. Размер больше длины даёт одну порцию. При неположительном размере возникает ValueError, причём у генераторной функции её тело начинает выполняться при обходе, а не при получении объекта генератора. Проверяйте ошибку через list(batches([1], 0)). Для потока без известной длины понадобится другая реализация.

Как оценить готовность к техническому интервью

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

Для тренировки вслух выберите наставника по Python и заранее отправьте вакансию, своё решение и вопросы. Полезный результат такого разбора: понятный список пробелов и следующая задача для самостоятельной работы. Заучивание готовых ответов не заменяет понимание собственного кода.

Похожие статьи по данной теме