Вопросы про импорты и окружения кажутся скучными, но именно на них сыпятся при разборе «почему у меня работает, а в CI нет». Middle-разработчик должен объяснить, как Python находит модуль и как изолировать зависимости.
Модуль и пакет
- Модуль это файл
.py. При первом импорте Python выполняет его код один раз и кладёт объект модуля вsys.modules. Повторныйimportберёт готовый объект из кеша. - Пакет это каталог с модулями. Обычный пакет содержит
__init__.py(он выполняется при импорте пакета). Без него каталог становится namespace-пакетом, который может быть собран из нескольких мест.
Как Python ищет модуль
import x смотрит:
sys.modules: уже загружен?- Встроенные и замороженные модули.
- Пути из
sys.pathпо порядку: каталог запускаемого скрипта (или текущий приpython -m),PYTHONPATH, стандартная библиотека,site-packagesактивного окружения.
Отсюда классическая ошибка: файл random.py или requests.py в проекте перекрывает настоящий модуль.
if name == 'main'
У запускаемого файла __name__ равен '__main__', у импортированного это имя модуля. Блок под условием выполняется только при прямом запуске, а не при импорте. python -m package.module запускает модуль как скрипт, сохраняя корректные пакетные импорты.
Абсолютные и относительные импорты
from app.services.billing import charge # абсолютный
from .billing import charge # относительный, только внутри пакета
from ..models import User
Относительные импорты работают только в модулях, загруженных как часть пакета. Запуск такого файла напрямую (python app/services/x.py) даёт ImportError: attempted relative import with no known parent package.
Циклические импорты
a импортирует b, b импортирует a. Пока a выполняется, он уже лежит в sys.modules, но недоделанным. Если b делает from a import func, а func ещё не определена, будет ImportError. Лечат архитектурно: выносят общее в третий модуль, переносят импорт внутрь функции, импортируют модуль целиком (import a и обращение a.func в момент вызова).
Виртуальные окружения
Окружение это каталог со своим интерпретатором (ссылкой на него) и своим site-packages. Оно изолирует зависимости проектов друг от друга и от системного Python.
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Активация лишь меняет PATH, чтобы python и pip брались из .venv.
Управление зависимостями в 2026
- pyproject.toml (PEP 621) единое место для метаданных проекта, зависимостей и настроек инструментов (ruff, pytest, mypy).
- uv быстрый менеджер на Rust: окружения, установка, lock-файл
uv.lock, управление версиями Python. Стал стандартом де-факто для новых проектов. - Poetry и pip-tools по-прежнему встречаются.
- Lock-файл фиксирует точные версии всех транзитивных зависимостей, чтобы сборка была воспроизводимой. В
pyprojectпишут диапазоны, в lock точные версии.
Частые ошибки
- Свой файл с именем модуля стандартной библиотеки.
pip installв системный Python вместо окружения.- Нет lock-файла: сборка сегодня и завтра ставит разные версии.
- Тяжёлый код на верхнем уровне модуля: он выполнится при любом импорте.
- Изменение модуля и ожидание, что повторный
importего перечитает (нуженimportlib.reload, и то с оговорками).
Что спросят на собесе
- Что происходит при
import, сколько раз выполняется код модуля. - Зачем
if __name__ == '__main__'. - Как Python ищет модули, что такое
sys.path. - Как бороться с циклическими импортами.
- Зачем виртуальное окружение, чем lock-файл отличается от списка зависимостей, что вы используете: pip, Poetry, uv.