Виртуальные окружения: venv и изоляция зависимостей

Виртуальные окружения: venv и изоляция зависимостей

В прошлом уроке мы запускали скрипты на чистом Python без сторонних библиотек. В реальных проектах сторонних пакетов десятки: FastAPI, Pydantic, SQLAlchemy, pytest, requests. Если ставить их прямо в системный Python - быстро возникает каша версий, и проекты ломают друг друга. Решение - виртуальные окружения.

Проблема общего Python

Представь две задачи на одной машине:

  • Проект A использует pydantic==1.10 (старый legacy)
  • Проект B использует pydantic==2.6 (новый, не совместим по API)

Если оба ставят зависимости в один и тот же python3, второй pip install pydantic==2.6 перезапишет первый, и проект A сломается. Это типичная проблема dependency hell.

Виртуальное окружение - отдельная директория с собственным интерпретатором и набором пакетов, изолированная от системы и других проектов.

Если ты знаком с [Docker](../docker/01-why-docker.md) - venv похож на лёгкий контейнер только для пакетов Python. Если знаком с Go - это аналог `go.mod` с локальным `vendor/`, только обязательный.

Создаём venv

Стандартный инструмент - модуль venv из стандартной библиотеки. Он не требует установки.

# В корне проекта
python3 -m venv .venv

Команда создаёт директорию .venv/ со структурой:

Структура виртуального окружения .venv: корневая директория с bin/, lib/, pyvenv.cfg; в bin/ лежат python, pip, activate

  • bin/ (на Windows - Scripts/) - исполняемые файлы окружения: симлинк на интерпретатор, pip для установки пакетов, activate для активации
  • lib/ - сюда pip install кладёт пакеты, изолированно от системного Python
  • pyvenv.cfg - конфигурация: на какой базовый Python ссылается окружение, version, флаги
Принятая по проектам практика - называть `.venv` или `venv`. Имя должно быть добавлено в `.gitignore` - окружение не коммитим, оно генерируется из `requirements.txt` или `pyproject.toml`.

Активация и деактивация

После создания окружение нужно активировать в текущем терминале:

source .venv/bin/activate
source .venv/bin/activate
.venv\Scripts\Activate.ps1

В cmd:

.venv\Scripts\activate.bat

После активации в приглашении терминала появится префикс (.venv). Это значит что python и pip теперь указывают на исполняемые файлы внутри окружения:

(.venv) $ which python
/path/to/project/.venv/bin/python

(.venv) $ which pip
/path/to/project/.venv/bin/pip

Для выхода:

deactivate

Ставим первый пакет

С активным venv установка не загрязняет систему:

(.venv) $ pip install requests
Collecting requests
  Downloading requests-2.31.0-py3-none-any.whl
Installing collected packages: requests
Successfully installed requests-2.31.0

Проверь:

(.venv) $ pip list
Package    Version
---------- -------
pip        23.x.x
requests   2.31.0
setuptools 65.x.x

Используем в коде:

import requests

response = requests.get("https://api.github.com/zen")
print(response.text)

Запускаем python script.py - всё работает. Деактивируешь окружение и запускаешь тот же скрипт системным python - получишь ModuleNotFoundError. Это и есть изоляция.

Фиксируем зависимости: requirements.txt

Чтобы другой разработчик (или CI, или продакшн) повторил окружение, нужно зафиксировать список пакетов:

(.venv) $ pip freeze > requirements.txt

Файл выглядит так:

certifi==2024.2.2
charset-normalizer==3.3.2
idna==3.6
requests==2.31.0
urllib3==2.2.0

Все транзитивные зависимости явно зафиксированы. Это и плюс (полная воспроизводимость), и минус (requirements.txt шумный).

Восстановить окружение по файлу:

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
`pip freeze > requirements.txt` - простой, но грубый подход. В современных проектах предпочитают `pyproject.toml` с инструментами вроде `poetry` или `uv`, которые разделяют прямые и транзитивные зависимости. Подробнее в [следующем уроке про pip и pyproject.toml](./04-pip-pyproject.md).

Файл .gitignore

В каждый Python-проект сразу добавляй базовый .gitignore:

# Виртуальное окружение
.venv/
venv/

# Кеш интерпретатора
__pycache__/
*.pyc
*.pyo

# Файлы IDE
.idea/
.vscode/

# Переменные окружения
.env

# Артефакты сборки
dist/
build/
*.egg-info/

Без него легко закоммитить .venv/ весом в сотни мегабайт, что испортит репозиторий и потребует чистки истории.

Распространённые ошибки

Ошибка 1: ставить пакеты в системный Python. Если ты вызвал pip install something без активного venv - пакет улёгся в системный Python. На некоторых системах это даже требует sudo, что вообще ломает права. Решение - всегда работать в venv.

Ошибка 2: коммитить .venv/. Окружение - артефакт сборки, оно платформо-зависимое (Linux-окружение не запустится на Windows). Коммитят только requirements.txt или pyproject.toml.

Ошибка 3: забыть активировать venv. Запускаешь python script.py без активации - используется системный интерпретатор, ничего не находит. Проверка: which python (Linux/macOS) или where python (Windows) - путь должен указывать в .venv/.

Ошибка 4: использовать pip3 после активации. После активации pip уже указывает на исполняемый файл внутри venv. Писать pip3 не нужно, обычный pip работает корректно. Это путает: некоторые гайды показывают pip3 для системного Python.

Альтернативы venv

В экосистеме есть несколько менеджеров окружений и зависимостей:

ИнструментЧто делает
venvСтандартный, минималистичный. Только окружения.
virtualenvСтарый аналог venv, теперь почти не нужен (venv достаточен)
condaОкружения + бинарные пакеты (для data science, не для backend)
pyenvУстановка нескольких версий Python (управление интерпретаторами, не пакетами)
poetryСовременный: окружение + зависимости + сборка + публикация
uvНовый быстрый менеджер от Astral, drop-in замена pip/venv

Для backend-разработки в 2026 году лучший выбор - venv + poetry или uv. Базовый venv нужно знать в любом случае - на нём построены все остальные инструменты.

Мини-задание

  1. В новой пустой директории создай venv и активируй его:
mkdir myproject && cd myproject
python3 -m venv .venv
source .venv/bin/activate
  1. Проверь куда указывает python:
which python
which pip
  1. Установи библиотеку requests, напиши скрипт fetch.py:
import requests
print(requests.get("https://api.github.com/zen").text)

Запусти и убедись что выводится цитата.

  1. Зафиксируй зависимости: pip freeze > requirements.txt. Открой файл - убедись что requests и его транзитивные зависимости в списке.

  2. Деактивируй (deactivate), удали .venv/, восстанови окружение из requirements.txt. Убедись что скрипт по-прежнему работает.

Что дальше

Поняли как изолировать окружение и фиксировать зависимости. Тот же .venv будет нужен, когда дойдём до запуска тестов через pytest. В следующем уроке углубимся в pip и pyproject.toml: чем отличается pip install от добавления зависимости в файл, и почему всё больше проектов отказываются от requirements.txt в пользу декларативного pyproject.toml.

Зарегистрируйтесь бесплатно, чтобы пройти квиз, решить задание с автопроверкой и вести прогресс.