Прежде чем писать на 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-коде.
- Функции и модули — как организовать код в функции, модули и пакеты.