Урок по треку Python 6 мин

Модули, пакеты и виртуальные окружения в Python

Система импорта, __name__, пакеты, циклические импорты, venv, pip, uv, pyproject.toml.

Вопросы про импорты и окружения кажутся скучными, но именно на них сыпятся при разборе «почему у меня работает, а в CI нет». Middle-разработчик должен объяснить, как Python находит модуль и как изолировать зависимости.

Модуль и пакет

  • Модуль это файл .py. При первом импорте Python выполняет его код один раз и кладёт объект модуля в sys.modules. Повторный import берёт готовый объект из кеша.
  • Пакет это каталог с модулями. Обычный пакет содержит __init__.py (он выполняется при импорте пакета). Без него каталог становится namespace-пакетом, который может быть собран из нескольких мест.

Как Python ищет модуль

import x смотрит:

  1. sys.modules: уже загружен?
  2. Встроенные и замороженные модули.
  3. Пути из 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.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.