Deep Engineering

Python · Подготовка к собеседованию

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

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

  1. 01

    Классы и ООП

    Класс хранит общие для экземпляров атрибуты и методы, экземпляр обычного класса — свои данные в собственном словаре. Метод — функция в классе, self — её первый аргумент: a.m() равносильно A.m(a). Чтение обычного атрибута ищет в экземпляре, потом в классе; присваивание через экземпляр пишет в экземпляр. @classmethod получает класс, @staticmethod — ничего.

    Полный ответ — в уроке →

    Дальше спросят

  2. 02

    Встроенные коллекции

    Список — упорядоченный изменяемый набор с повторами, кортеж — неизменяемый, словарь — ключ → значение с уникальными ключами, множество — уникальные элементы без порядка. Проверку вхождения и поиск по значению делают по множеству или словарю: там это поиск по хешу — O(1) в среднем и O(n) в худшем случае, когда хеши совпадают, — а в списке — перебор. Элементом множества и ключом словаря может быть только хешируемый объект; значения словаря могут быть любыми. Словарь хранит порядок вставки, множество — нет.

    Полный ответ — в уроке →

    Дальше спросят

  3. 03

    Модули и импорт

    import находит модуль и связывает имя. Тело модуля выполняется при первой успешной загрузке; повторный import под тем же полным именем в том же интерпретаторе берёт модуль из словаря sys.modules. from m import x копирует ссылку на значение в момент импорта — новое присваивание m.x её не меняет. if __name__ == "__main__" отделяет код запуска от кода, который выполнится при импорте.

    Полный ответ — в уроке →

    Дальше спросят

  4. 04

    Сложность операций

    O-нотация описывает, как растёт работа с ростом входа; строго это верхняя оценка. x in list, insert(0, x), remove — O(n); x in set, d[key] — O(1) в среднем и O(n) в худшем случае, когда хеши совпадают; append — амортизированное O(1); bisect — O(log n); sorted() — O(n log n) в среднем и худшем случае. Сложность кода — это число итераций, умноженное на стоимость операции внутри.

    Полный ответ — в уроке →

    Дальше спросят

  5. 05

    Строки и форматирование

    Строка неизменяема — методы и += возвращают результат, а исходная строка не меняется. Срезы s[i:j:k] не включают правую границу, отрицательные индексы считаются с конца, границы среза урезаются до длины строки — поэтому срез целиком за её пределами пуст. Форматируют f-строками со спецификацией после двоеточия. Строку из многих кусков собирают через "".join(...): документация называет повторную склейку квадратичной, а быстрый путь CPython — условная оптимизация. str — кодовые точки Юникода, bytes — байты, между ними — encode и decode.

    Полный ответ — в уроке →

    Дальше спросят

  6. 06

    Тестирование

    Тест вызывает код и проверяет результат; в unittest FAIL — сработавшая проверка (AssertionError), ERROR — любое другое исключение. Внешние зависимости подменяют Mock через patch — в том модуле, где имя используется. Главное утверждение теста — о результате кода; проверка вызова mock — дополнительная. Моки создают по настоящему объекту: Mock(spec=…) отвергает несуществующие имена, а неверный вызов ловит только create_autospec или autospec=True. Тесты независимы: на порядок их выполнения не опираются, состояние готовится в setUp или фикстуре.

    Полный ответ — в уроке →

    Дальше спросят

  7. 07

    Потоки и процессы

    Потоки делят память процесса, процессы изолированы. В обычном CPython — сборке с GIL — потоки выполняют Python-код по очереди, поэтому для ожидания — сети, диска — берут потоки или asyncio, а для вычислений на Python — процессы; расширения на C могут отпускать GIL сами. Процессам задачи передают через pickle, запуск процессов дороже, а пул процессов создают в main() под if __name__ == "__main__":. GIL не избавляет от гонок: общее состояние защищают Lock.

    Полный ответ — в уроке →

    Дальше спросят

  8. 08

    Окружения и зависимости

    Виртуальное окружение — папка со своим интерпретатором и своим site-packages; создаётся python -m venv. Интерпретатор окружения находит его сам по pyvenv.cfg, а activate нужен оболочке — через PATH команда python находит интерпретатор окружения. Пакеты ставят в окружение через python -m pip, а установленные версии записывают в requirements.txt через pip freeze — это снимок установленного, а не lock-файл. В системный Python пакеты не ставят: окружение изолирует версии проектов.

    Полный ответ — в уроке →

    Дальше спросят

  9. 09

    Декораторы

    @d над def f — это f = d(f), выполненное один раз, в момент, когда интерпретатор дошёл до def. Когда декораторов несколько, выражения после @ вычисляются сверху вниз, а применяются снизу вверх. functools.wraps делает обёртку похожей на цель, а не равной ей: сигнатуру цели показывает inspect, идущий по __wrapped__, а сама обёртка по-прежнему принимает *args, **kwargs.

    Полный ответ — в уроке →

    Дальше спросят

  10. 10

    Дескрипторы

    Дескриптор — это объект, который лежит в классе и вмешивается в обращение к атрибуту экземпляра; признак один — у его типа есть __get__, __set__ или __delete__. Всё поведение выводится из одной строки приоритета: дескриптор данных (есть __set__ или __delete__) сильнее словаря экземпляра, дескриптор не-данных (только __get__) — слабее. property, обычный метод, staticmethod, classmethod, слот и cached_property уже дескрипторы.

    Полный ответ — в уроке →

    Дальше спросят

  11. 11

    Итераторы и генераторы

    Итерируемое умеет отдать итератор (__iter__), итератор умеет отдать следующий элемент (__next__) и обязан отдавать себя из __iter__. Разница в одном методе, и в поведении она тоже одна: список обходится дважды, итератор — нет. Генератор — это итератор, написанный как функция, и вызов генераторной функции тело не выполняет: он создаёт объект и возвращает управление.

    Полный ответ — в уроке →

    Дальше спросят

  12. 12

    Контекстные менеджеры

    with — это гарантия того, что выход из блока будет обработан, чем бы блок ни кончился. Протокол — два метода на типе: __enter__ вызывается на входе, __exit__ на выходе, и as связывает то, что вернул __enter__, а не сам объект.

    Полный ответ — в уроке →

    Дальше спросят

  13. 13

    Исключения и finally

    try — попытка, except — обработка ошибки, else — ветка «ошибки не было», finally — выход в любом случае. И главное про finally: если он сам передаёт управление наружу — return, break, continue, — летящее исключение исчезает. Справочник формулирует это одним предложением: «сохранённое исключение отбрасывается».

    Полный ответ — в уроке →

    Дальше спросят

  14. 14

    lambda

    lambda — та же функция, записанная выражением. Объект получается того же типа, что и у def, с тем же байт-кодом тела; пишут её там, где функция нужна прямо в месте вызова и отдельное имя ухудшило бы читаемость.

    Полный ответ — в уроке →

    Дальше спросят

  15. 15

    async/await

    Вызов корутинной функции (async def без yield) не выполняет её тело — он только создаёт объект coroutine. Тело исполняет тот, кто этот объект ведёт: await в текущей задаче, asyncio.run или задача из create_task, и задача может начать работу раньше, чем дело дойдёт до await task. await уступает управление другим задачам, только если ожидаемое ещё не готово: await завершившегося Future хода не передаёт. Отсюда забытый await: if is_allowed(user) проверяет на истинность объект корутины вместо результата проверки и проходит при любых данных.

    Полный ответ — в уроке →

    Дальше спросят

  16. 16

    *args и **kwargs

    Звёздочка в объявлении собирает, звёздочка в вызове раскладывает. Одна собирает лишние позиционные аргументы в кортеж args, две — лишние именованные в словарь kwargs; в вызове одна раскладывает последовательность в позиционные аргументы, две — словарь в именованные. И оба объекта создаются, даже когда передавать нечего: приходят () и {}, а не None.

    Полный ответ — в уроке →

    Дальше спросят

  17. 17

    Список против кортежа

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

    Полный ответ — в уроке →

    Дальше спросят

  18. 18

    Включения

    Включение — это цикл, записанный одним выражением, но выполняется оно в собственной области видимости, и граница этой области проходит внутри квадратных скобок, а не по ним. Всё, кроме выражения-источника в самом левом for, исполняется в отдельной неявно вложенной области — поэтому переменная включения наружу не течёт, а само включение не всегда видит те же имена, что окружающий его код.

    Полный ответ — в уроке →

    Дальше спросят

  19. 19

    Замыкания

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

    Полный ответ — в уроке →

    Дальше спросят

  20. 20

    MRO

    MRO — порядок, в котором Python ищет атрибут по классам иерархии; он лежит в Class.__mro__ и строится алгоритмом C3. C3 ставит класс раньше его предков и сохраняет порядок баз, поэтому в ромбе D(Left, Right) общий предок уходит в хвост: D, Left, Right, Base, object. А super() — посредник: поиск через него идёт по MRO экземпляра, начиная после класса, где написан метод, и это не обязательно родитель.

    Полный ответ — в уроке →

    Дальше спросят

  21. 21

    Копирование

    Присваивание не копирует ничего — это второе имя для того же объекта; copy.copy создаёт новый верхний объект и оставляет вложенные общими; copy.deepcopy добирается и до вложенных. Разница видна только тогда, когда меняют вложенный объект: после поверхностной копии изменение видно через обе стороны, после глубокой — нет.

    Полный ответ — в уроке →

    Дальше спросят

  22. 22

    is против ==

    is спрашивает, один ли это объект; его нельзя переопределить, и его цена не зависит от объекта. == вызывает __eq__, обычный метод, который может вернуть что угодно. is уместен там, где контракт API именно про тождество: у любого объекта-одиночки и у любой метки, заведённой ради такого сравнения, — None, NotImplemented, Ellipsis, члены Enum, собственный часовой object().

    Полный ответ — в уроке →

    Дальше спросят

  23. 23

    Сборка мусора

    В CPython память освобождается прежде всего подсчётом ссылок: объект умирает, как только на него пропадает последняя ссылка, сразу. del удаляет имя, а не объект. Сборщик мусора дополняет счётчик и нужен для циклов: объекты, ссылающиеся друг на друга, счётчик не освободит никогда, и gc находит такие группы без внешних ссылок.

    Полный ответ — в уроке →

    Дальше спросят

  24. 24

    Метаклассы

    Метакласс — это тип класса. Класс — экземпляр метакласса ровно так же, как объект — экземпляр класса, и метакласс управляет созданием классов примерно так же, как класс управляет созданием экземпляров. Отсюда и то, что Class(...) — это вызов __call__ у метакласса: класс — его экземпляр.

    Полный ответ — в уроке →

    Дальше спросят

  25. 25

    Аргумент по умолчанию

    Выражение после = вычисляется один раз, при выполнении def, и получившийся объект достаётся каждому вызову, в котором этот аргумент не передан. Поэтому изменяемое значение по умолчанию накапливает изменения между вызовами. Чинится проверкой на None; своя метка нужна только там, где None — допустимое значение аргумента.

    Полный ответ — в уроке →

    Дальше спросят

← Карта линии и итоговая проверка