← назад к разделу

Прежде чем писать на Python код, который кто-то будет запускать кроме вас, нужно разобраться с тем, как этот код вообще запускается и где живут его зависимости. Эта статья — про рабочую среду: интерпретатор, виртуальные окружения, установку библиотек, описание проекта и автоматическое форматирование.

Зачем вообще нужны инструменты

Python — интерпретируемый язык: исходный код не компилируется в отдельный исполняемый файл заранее, а читается и выполняется специальной программой — интерпретатором. Когда вы пишете python script.py, операционная система запускает интерпретатор, а он уже построчно исполняет ваш файл.

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

Интерпретатор и версии

«Python» как программа — это и есть интерпретатор. У него есть версия, и она важна: синтаксис и стандартная библиотека меняются от версии к версии. Актуальная линейка на момент статьи — Python 3.12.

Проверить, какая версия установлена:

python --version        # например: Python 3.12.3
python3 --version       # на macOS/Linux часто нужен именно python3

Важная тонкость: в системе обычно живёт несколько интерпретаторов одновременно. Системный Python (которым пользуется сама ОС), Python, который вы поставили сами, версии из разных проектов. Команда python указывает на какой-то один из них — на какой именно, зависит от настроек окружения. Поэтому первое правило: никогда не ставьте библиотеки в системный Python — можно сломать инструменты операционной системы. Для каждого проекта заводят отдельное изолированное окружение.

Если на машине нужно держать несколько версий самого интерпретатора (например, 3.11 для одного проекта и 3.12 для другого), для этого есть отдельные менеджеры версий (pyenv и подобные) — но для начала достаточно одной свежей версии.

Виртуальные окружения (venv)

Виртуальное окружение — это отдельная папка с собственной копией интерпретатора и собственным набором установленных библиотек. Проекты в разных окружениях не видят библиотеки друг друга, поэтому их зависимости не конфликтуют.

Короткая формула: одно окружение — один проект.

Создаётся окружение встроенным модулем venv:

# создаём окружение в папке .venv внутри проекта
python -m venv .venv

# активируем его (macOS/Linux)
source .venv/bin/activate

# активируем его (Windows)
.venv\Scripts\activate

После активации в начале строки терминала появляется (.venv), а команда python начинает указывать на интерпретатор из этого окружения. Всё, что вы теперь установите, попадёт в .venv, а не в систему. Выйти из окружения — командой deactivate.

Папку .venv не кладут в систему контроля версий (её добавляют в .gitignore): окружение легко пересоздать из описания зависимостей, а весит оно много.

pip и зависимости

pip — это менеджер пакетов Python. Он скачивает библиотеки из центрального репозитория PyPI (Python Package Index) и устанавливает их в активное окружение.

pip install requests              # поставить библиотеку
pip install "requests==2.31.0"    # конкретную версию
pip uninstall requests            # удалить
pip list                          # что установлено в окружении

Чтобы проект мог собрать кто-то другой (или вы на другой машине), список зависимостей нужно зафиксировать. Классический способ — файл requirements.txt:

# выгрузить текущие зависимости в файл
pip freeze > requirements.txt

# установить всё из файла в новое окружение
pip install -r requirements.txt

Так воспроизводится среда: создал окружение → поставил зависимости из файла → код работает так же, как у автора. Сегодня вместо «голого» requirements.txt всё чаще описывают зависимости в pyproject.toml — о нём дальше.

pyproject.toml и структура пакета

pyproject.toml — это стандартный файл описания Python-проекта. Один файл в формате TOML, в котором собрано всё: имя и версия проекта, его зависимости, а также настройки инструментов (того же форматтера и линтера). Он пришёл на смену разрозненным setup.py, setup.cfg и отдельным конфигам.

Минимальный пример:

[project]
name = "my-service"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
    "requests>=2.31",
    "fastapi>=0.110",
]

[tool.ruff]
line-length = 100

Секция [project] описывает сам проект и его зависимости (dependencies). Секции вида [tool.<имя>] — это настройки конкретных инструментов; они живут в том же файле, поэтому конфигурация проекта не растекается по десятку файлов.

Типичная структура пакета для backend-проекта выглядит так:

my-service/
├── pyproject.toml         # описание проекта и зависимостей
├── README.md
├── src/
│   └── my_service/        # сам пакет (папка с __init__.py)
│       ├── __init__.py
│       └── main.py
└── tests/
    └── test_main.py

Пакет — это просто папка с файлом __init__.py (он может быть пустым); такая папка становится единицей импорта. Раскладка с папкой src/ — общепринятая: она не даёт случайно импортировать код «из текущей директории» вместо установленного пакета и делает тесты честнее.

Форматтеры и линтеры (ruff, black)

Два разных инструмента, которые часто путают:

  • Форматтер автоматически приводит код к единому виду — расставляет отступы, кавычки, переносы строк. Он не меняет смысл кода, только внешний вид. Самый известный — black.
  • Линтер ищет проблемы: неиспользованные импорты, подозрительные конструкции, нарушения стиля. Он не переписывает код (хотя многое умеет чинить), а указывает на ошибки. Сегодня стандарт — ruff: он очень быстрый и заменяет сразу множество старых инструментов, а с недавних пор умеет и форматировать.
ruff check .          # проверить код на проблемы
ruff check --fix .    # проверить и починить то, что можно
ruff format .         # отформатировать (аналог black)

black .               # отформатировать через black

Зачем это нужно: единый стиль убирает споры о форматировании в команде и делает изменения в истории проекта чистыми (в правках виден смысл, а не перестановка кавычек). Настройки задаются в pyproject.toml — в секциях [tool.ruff] и [tool.black].

Запуск скриптов и модулей

Запустить файл напрямую — самый очевидный способ:

python script.py            # выполнить файл script.py

Но у Python есть второй, более «правильный» для пакетов способ — запуск модуля через флаг -m:

python -m my_service.main   # запустить модуль внутри пакета
python -m venv .venv        # так же запускается сам venv
python -m pip install ...   # и pip — это тоже модуль

Разница принципиальна. При python script.py интерпретатор считает «корнем» папку самого файла. При python -m имя.модуля корнем считается текущая директория, и Python ищет модуль как часть пакета — поэтому внутренние импорты пакета работают корректно. Для одиночных скриптов годится первый способ, для кода внутри пакета — почти всегда -m.

Отдельно стоит знать про конструкцию:

def main() -> None:
    print("привет")

if __name__ == "__main__":   # выполнится только при прямом запуске файла
    main()

Переменная __name__ равна "__main__", когда файл запущен напрямую, и равна имени модуля, когда файл импортирован из другого кода. Этот приём позволяет одному и тому же файлу быть и запускаемым скриптом, и импортируемым модулем без побочных эффектов при импорте.

Коротко

  • Python интерпретируемый: код выполняет интерпретатор, и важно, какой именно и с какими библиотеками. Актуальная версия — 3.12.
  • Виртуальное окружение (python -m venv .venv + активация) изолирует зависимости проекта; правило — одно окружение на проект, в систему библиотеки не ставим.
  • pip ставит пакеты из PyPI; список зависимостей фиксируют, чтобы среду можно было воспроизвести.
  • pyproject.toml — единый файл описания проекта: имя, версия, зависимости и настройки инструментов.
  • Пакет — папка с __init__.py; типичная раскладка — src/<пакет>/ и tests/.
  • ruff (линтер + форматтер) и black (форматтер) держат код в едином стиле; настройки — в pyproject.toml.
  • Запуск: python script.py для одиночных файлов, python -m пакет.модуль для кода внутри пакета; блок if __name__ == "__main__": делает файл и скриптом, и модулем.

Что почитать дальше

  • Синтаксис и типы данных — базовый синтаксис, переменные и встроенные типы.
  • Аннотации типов (type hints) — как добавлять типы и зачем они нужны в backend-коде.
  • Функции и модули — как организовать код в функции, модули и пакеты.