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

Как работает импорт модулей и что делать с циклическими импортами?

MiddleОкружение и инструменты
Ещё спрашивают так:
  • Что происходит при import в Python?
  • Зачем нужен __init__.py?
Коротко

При первом import модуль ищется по sys.path, выполняется сверху вниз один раз и кладётся в sys.modules; повторные импорты берут его оттуда. Циклический импорт ломается, когда модуль A ещё не дошёл до нужного имени, а B уже пытается его взять — ImportError: cannot import name. Лечат разделением модулей, импортом модуля вместо имени или переносом импорта внутрь функции.

  • __init__.py делает папку обычным пакетом и выполняется при его импорте
  • Без __init__.py — namespace package (PEP 420)
  • Код на уровне модуля выполняется при импорте — не делать там тяжёлых действий

А ещё if TYPE_CHECKING: — импорт только для аннотаций, без цикла во время выполнения

Подробно

Шаги import X:

  1. проверить sys.modules — если есть, вернуть;
  2. найти модуль через искатели (sys.meta_path) по sys.path: папка скрипта, PYTHONPATH, site-packages;
  3. создать объект модуля, положить в sys.modules до выполнения;
  4. выполнить код модуля.

Почему ломается цикл. a.py начинает выполняться, импортирует b; b.py делает from a import helper — a уже в sys.modules, но выполнен только до строки импорта, helper ещё нет.

Решения:

  • вынести общее в третий модуль (часто цикл — признак плохого разбиения);
  • import a и обращение a.helper при вызове, а не при импорте;
  • импорт внутри функции;
  • для аннотаций — from __future__ import annotations и импорт под if TYPE_CHECKING:.

Абсолютные и относительные: from .models import User — внутри пакета; запуск файла пакета как скрипта ломает относительные импорты — использовать python -m package.module.

Что хотят услышать

  • Механика импорта и sys.modules.
  • Причина и решения циклических импортов.
  • Роль __init__.py.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✗Модуль выполняется заново при каждом import, поэтому глобальные объекты в нём не общие
  • ✗Циклический импорт невозможен — Python запрещает его при компиляции
  • ✓Модуль выполняется один раз и кэшируется в sys.modules; циклы лечат разделением модулей
  • ✗Циклы лечат, импортируя все модули в __init__.py пакета

Разбор темы

Связанные вопросы

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования