Вопросы по Python на собеседовании: ядро языка
Вопросы по ядру языка задают всем, кто пишет на Python, независимо от роли. Любимый приём интервьюера это короткий фрагмент кода с изменяемым объектом: по объяснению результата сразу видно, понимает ли человек модель данных Python.
Из чего состоит тема
Так тема разложена в тренажёре: движок ведёт прогресс по каждой подтеме отдельно и возвращает те, где вы ошибаетесь.
- ООП22
- Исключения и контекст19
- Структуры данных19
- Функции и области видимости18
- Итераторы и генераторы15
- Стандартная библиотека и строки14
- Типизация, GIL, окружение13
- Модель данных и ссылки12
Разборы подтем
Конспект по каждой: что это, как отвечать вслух, на чём валятся, плюс вопросы для самопроверки.
- Python: модель данных и ссылки12 вопросов
- Стандартная библиотека и строки14 вопросов
- Типизация, GIL, окружение13 вопросов
Примеры вопросов с разбором
- Кортеж (tuple) неизменяем. Значит ли это, что содержимое, лежащее внутри него, тоже нельзя изменить никак?A)Да, кортеж заморожен, и вложенные в него объекты автоматически тоже становятся неизменяемымиB)Нет, элементы кортежа можно свободно переприсваивать по индексу, как у списка, — неизменяемость этого совсем не запрещаетC)Нельзя менять сам кортеж (переприсвоить элемент), но изменяемый объект внутри него, например список, менять можноD)Кортеж вообще не хранит объекты, а лишь их строковые представления, поэтому вопрос об их изменении не имеет смысла
показать ответ и разбор
+C)Нельзя менять сам кортеж (переприсвоить элемент), но изменяемый объект внутри него, например список, менять можно// разбор: Неизменяемость кортежа — про его структуру: нельзя переприсвоить t[0] или добавить элемент. Но если элемент сам изменяемый (список), его содержимое менять можно: t=([1],); t[0].append(2) сработает, а t[0]=[] — нет. Отсюда же следствие: кортеж со списком внутри нельзя хешировать, то есть класть ключом в словарь.
- Сохраняет ли обычный dict порядок вставки ключей в современном Python?A)Да, начиная с Python 3.7 обычный dict гарантированно сохраняет порядок вставки ключей как часть спецификации языкаB)Нет, порядок ключей в dict полностью случаен и меняется при каждом новом запуске одной и той же программыC)Порядок сохраняется исключительно у специального OrderedDict, а обычный dict его не обеспечивает даже в новых версиях языкаD)Dict хранит ключи строго в отсортированном по возрастанию порядке независимо от того, в каком порядке их туда вставляли
показать ответ и разбор
+A)Да, начиная с Python 3.7 обычный dict гарантированно сохраняет порядок вставки ключей как часть спецификации языка// разбор: С Python 3.7 сохранение порядка вставки — часть спецификации языка (в 3.6 это было деталью реализации CPython). Поэтому обычный dict итерируется в порядке добавления ключей. OrderedDict теперь нужен редко — ради дополнительных операций вроде move_to_end и равенства с учётом порядка. Порядок вставки — не сортировка.
- Зачем нужен context manager (конструкция with) в Python?A)Гарантированно освобождать ресурс (файл, соединение) даже при исключенииB)Существенно ускорять выполнение цикла за счёт параллельного запуска всех его итерацийC)Автоматически кэшировать результат функции по её аргументамD)Превращать обычную функцию в генератор значений по одному
показать ответ и разбор
+A)Гарантированно освобождать ресурс (файл, соединение) даже при исключении// разбор: Context manager (протокол __enter__/__exit__, синтаксис with) гарантирует освобождение ресурса — закрытие файла, откат/коммит транзакции, освобождение блокировки — что бы ни случилось в блоке, включая исключение. Это надёжнее ручных open/close, где на пути к close легко словить ошибку и оставить ресурс висеть.
- Что делают *args и **kwargs в сигнатуре функции?A)*args собирает произвольные позиционные аргументы в кортеж, а **kwargs — произвольные именованные аргументы в словарьB)Они задают строго обязательные первый и второй аргументы функции, без которых её не получится вызвать в коде вообще никакC)Они автоматически кешируют все переданные функции аргументы, чтобы при повторном вызове не пересчитывать её результат зановоD)Это специальные аннотации типов, которые проверяют, что все переданные функции аргументы являются числами или же строками
показать ответ и разбор
+A)*args собирает произвольные позиционные аргументы в кортеж, а **kwargs — произвольные именованные аргументы в словарь// разбор: *args ловит лишние позиционные аргументы в кортеж, **kwargs — лишние именованные в словарь. Они позволяют писать функции с переменным числом аргументов и прокидывать их дальше: wrapper(*args, **kwargs). В точке вызова те же звёздочки, наоборот, распаковывают коллекцию в отдельные аргументы: f(*список, **словарь).
- Что такое генератор в Python?A)Функция с yield, отдающая значения по одному лениво, без хранения всегоB)Обычная функция, которая сразу целиком возвращает весь готовый список за один вызовC)Класс для параллельного запуска задач сразу на нескольких ядрах CPUD)Специальный декоратор, кэширующий результат вызова функции по аргументам
показать ответ и разбор
+A)Функция с yield, отдающая значения по одному лениво, без хранения всего// разбор: Генератор — функция с yield, которая производит значения по требованию и не держит всю последовательность в памяти. Это позволяет обрабатывать потоки и файлы больше RAM в константной памяти. От обычной функции со списком он отличается ленивостью, от многопоточности и кэша (lru_cache) — принципиально.
- Чем атрибут класса отличается от атрибута экземпляра?A)Атрибут класса один общий на все экземпляры, атрибут экземпляра свой у каждого объекта; доступ ищет сперва на экземпляреB)Разницы нет никакой: оба варианта создают одинаковые независимые копии значения для каждого отдельного объекта классаC)Атрибут класса доступен лишь внутри методов класса, а атрибут экземпляра — снаружи, через точку у объектаD)Атрибут экземпляра существует лишь до конца метода __init__, после чего Python автоматически удаляет его из объекта совсем
показать ответ и разбор
+A)Атрибут класса один общий на все экземпляры, атрибут экземпляра свой у каждого объекта; доступ ищет сперва на экземпляре// разбор: Атрибут класса объявлен в теле класса и общий для всех экземпляров; атрибут экземпляра задаётся через self и свой у каждого объекта. Доступ obj.attr сначала ищет на экземпляре, затем на классе. Классическая грабля: изменяемый атрибут класса (например, список) разделяется всеми объектами — общее состояние; чтобы поле было своим у каждого, задавай его в __init__.
- Строки в Python неизменяемы. Почему тогда наращивание s += x в длинном цикле неэффективно?A)Каждое += создаёт новую строку и копирует всё накопленное — получается O(n²); части собирают в список и раз склеивают ''.join()B)Оно эффективно и оптимально: Python меняет строку прямо на месте, дописывая символы в конец без всякого копирования данныхC)Неэффективность вызвана тем, что оператор += для строк каждый раз обращается к диску для сохраненияD)Проблема в кодировке: += медленный лишь для строк с не-ASCII символами, а для латиницы работает быстро
показать ответ и разбор
+A)Каждое += создаёт новую строку и копирует всё накопленное — получается O(n²); части собирают в список и раз склеивают ''.join()// разбор: Строка неизменяема, поэтому s += x не дописывает на месте, а создаёт новую строку и копирует в неё всё прежнее содержимое. В длинном цикле это даёт квадратичную сложность по суммарной длине. Идиома — накапливать куски в список и один раз соединить: ''.join(parts). Это линейно и заметно быстрее.
- Зачем нужны виртуальное окружение и фиксация версий (pinning) зависимостей?A)Чтобы ускорить установку пакетов за счёт их параллельной загрузкиB)Изолировать зависимости проекта и воспроизводимо повторять сборку на другой машинеC)Чтобы автоматически исправлять баги в сторонних библиотеках проектаD)Чтобы отказаться от использования внешних библиотек
показать ответ и разбор
+B)Изолировать зависимости проекта и воспроизводимо повторять сборку на другой машине// разбор: Виртуальное окружение изолирует зависимости проекта от системных и других проектов, а lock-файл с точными версиями делает сборку воспроизводимой: та же среда развернётся на CI и на машине коллеги. Диапазоны версий («>=») коварны — обновление транзитивной зависимости молча ломает прод, поэтому в проде фиксируют точные версии.
- Почему список нельзя использовать как ключ словаря, а кортеж из чисел — обычно можно?A)Потому что список занимает слишком много памяти для ключа, а кортеж заметно компактнее по размеруB)Потому что ключами словаря в Python могут быть лишь строки и целые числа, остальное запрещеноC)Списки исторически запретили как ключи ради читаемости кода, никаких технических причин за этим ограничением не стоитD)Ключ должен быть хешируемым (по сути неизменяемым): у изменяемого списка нет стабильного хеша, у кортежа из чисел — есть
показать ответ и разбор
+D)Ключ должен быть хешируемым (по сути неизменяемым): у изменяемого списка нет стабильного хеша, у кортежа из чисел — есть// разбор: Словарь и множество хранят ключи по их хешу, а хеш обязан быть стабильным всё время, пока объект лежит в контейнере. Поэтому ключи должны быть хешируемыми, что для встроенных типов означает неизменяемость. Список изменяем — он unhashable. Кортеж из неизменяемых элементов хешируем; но кортеж со списком внутри — уже нет.
это 9 из 132
Ещё 123 вопросов по теме — в тренажёре, с движком повторения
Прочитать разбор и ответить самому — разные навыки. В Сеньорчике вопросы идут сессиями, а движок возвращает подтемы, где вы ошибаетесь, пока они не начнут отскакивать. Бесплатно, лимит по энергии.
Частые вопросы
Что чаще всего спрашивают по ядру Python?
Разницу изменяемых и неизменяемых типов, поведение аргументов по умолчанию, генераторы и ленивость, устройство словаря, обработку исключений и что делает with.
Дают ли писать код на собеседовании по Python?
Часто дают маленькие задачи или просят объяснить готовый фрагмент. Ценят не скорость набора, а умение вслух проследить, что произойдёт со ссылками и памятью.