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

PYTHON · TOOLING Фаза 2. Основы языка: Python

Обязательно

Инструменты Python: интерпретатор, venv, pip и pyproject.toml

CPython · venv · pyproject

Версии, изолированные зависимости, запуск модулей и форматирование.

1. Интерпретатор определяет запуск

1 / 5

python app.py запускает конкретную версию CPython. Проверка python --version показывает, какая именно.

app.pyCPythonspecific versionProgram result

2. venv изолирует проекты

2 / 5

Каждое окружение содержит собственный interpreter и site-packages. Библиотеки одного проекта не меняют другие.

Project A · venvFastAPI 0.115Project B · venvFastAPI 0.120separate site-packages

3. pip ставит в активное окружение

3 / 5

python -m pip install package использует pip выбранного interpreter. Это защищает от случайной установки в системный Python.

python -m pipActive venvsite-packagesPackage installed

4. pyproject описывает проект

4 / 5

Build system, зависимости, entry points и настройки инструментов хранятся в одном файле. Lock-файл фиксирует версии.

pyproject.tomldependenciesentry pointstool settingslockfile pins exact versions

5. Модули запускаются через -m

5 / 5

python -m package.module использует import path окружения. Ruff и black форматируют единый стиль перед CI.

python -mpackage.moduleimport pathRuff / Black

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

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

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

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

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

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

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

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

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

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

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

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

система /usr/bin/python3 site-packages системы проект A .venv/bin/python fastapi 0.142 проект B .venv/bin/python fastapi 0.110

У каждого проекта своя копия интерпретатора-ссылки и свои пакеты; две версии одной библиотеки на одной машине не мешают друг другу.

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

Создаётся окружение встроенным модулем 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.

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

live example

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

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

Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Three free days →

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

Где Python ищет модули: sys.path

Когда вы пишете import my_service.orders, интерпретатор перебирает список путей и берёт первый, где нашёлся пакет. Этот список называется sys.path, и половина ошибок ModuleNotFoundError объясняется тем, что в нём нет нужной папки:

live example

import sys

for path in sys.path:
    print(path)
Run

Running examples is part of paid access. There the same code runs inside the article: editor, run and check next to the paragraph. Three free days →

Первым элементом стоит каталог скрипта при python script.py или текущий каталог при python -m пакет.модуль, дальше стандартная библиотека и site-packages активного окружения, куда ставит пакеты pip. Поэтому python src/my_service/main.py ломает импорты внутри пакета: корнем становится src/my_service, а не src, и import my_service.orders не находится. Лечат это установкой проекта в окружение в режиме разработки (pip install -e .), после чего пакет виден отовсюду, или запуском через -m из корня. Переменная окружения PYTHONPATH добавляет каталоги в начало списка, и это аварийный инструмент для скриптов, а не способ собрать сервис.

Дополнительно: при первом чтении можно пропустить

Глубже: uv, lock-файлы и pipxрасширенное

Всё, что выше, можно делать одной программой. uv объединяет виртуальные окружения, установку пакетов, выбор версии самого Python и фиксацию зависимостей, и делает это на порядок быстрее pip:

uv init my-service           # pyproject.toml и каркас проекта
uv add fastapi               # записать зависимость и поставить её
uv sync                      # собрать окружение ровно по uv.lock
uv run python -m my_service.main

Файл uv.lock — это и есть то, чем pip freeze был в мире requirements: точные версии всех пакетов, включая транзитивные, одинаковые у всех разработчиков и в контейнере. Разница в том, что dependencies в pyproject.toml описывают намерение («любой FastAPI не ниже 0.110»), а lock-файл — факт («в этот раз поставили 0.137.2»). Без lock-файла сборка через полгода может подтянуть другие версии и сломаться на ровном месте.

Инструменты командной строки вроде ruff или httpie ставят не в окружение проекта, а отдельно: pipx install ruff или uv tool install ruff — тогда у них своё изолированное окружение, и их версии не спорят с зависимостями проекта. И последнее: когда непонятно, какой pip сработал, пишите python -m pip — это гарантированно pip того интерпретатора, который вы назвали.

Коротко

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

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