Окружения и зависимости: что делает venv, а что — pip
Вопросы про виртуальные окружения кажутся бытовыми, пока не спросят, как интерпретатор понимает, что он в окружении. Прогон отвечает: окружение — это папка со своим bin/python и файлом pyvenv.cfg; env/bin/python ставит sys.prefix на окружение без всякой активации; activate — скрипт оболочки, который ставит env/bin первым в PATH и задаёт VIRTUAL_ENV и VIRTUAL_ENV_PROMPT; пакет, поставленный в окружение, базовому интерпретатору не виден; pip freeze печатает снимок установленного, а не lock-файл; в свежем окружении на 3.11 два пакета — pip и setuptools, — а с 3.12 один pip; а с 3.14 sys.prefix окружения ставит инициализация путей, и python -S его уже не сбрасывает.
Полное техническое изложение
TL;DR
Виртуальное окружение — это отдельная папка со своим интерпретатором и
своим местом для пакетов. Его создаёт python -m venv env, а пакеты в него
ставит pip. Так у каждого проекта свои версии зависимостей, и установка
пакета для одного проекта не ломает другой.
Отсюда главное: окружение — это интерпретатор env/bin/python, а не
«включённый режим». В корне окружения, на уровень выше bin/python,
лежит pyvenv.cfg, и по этому файлу интерпретатор сам ставит sys.prefix —
каталог, который он считает своей установкой и от которого ищет пакеты, — на
окружение; в прогоне без всякой активации. activate нужен оболочке: он
кладёт env/bin первым в PATH, чтобы команда python нашла нужный
интерпретатор.
Дальше — то, что отличает понимание от пересказа. Пакет, поставленный в
окружение, базовому интерпретатору не виден: ModuleNotFoundError. pip freeze печатает снимок установленного в формате requirements: пакет из PyPI —
строкой имя==версия, editable- и локальная установка — строкой с путём; из
снимка делают requirements.txt. По нему ставятся те же версии, если они
доступны для той платформы и версии Python, — но это не lock-файл и не
побайтная копия. В свежем окружении на 3.11 два пакета, pip и setuptools,
а с 3.12 — один pip. С 3.13 python -m venv кладёт в окружение
.gitignore, и git не видит окружение. С 3.14 sys.prefix окружения ставит
инициализация путей, а не модуль site, и запуск с -S его уже не
сбрасывает.
- как запустить команду в терминале;
- что делает
importи что такое пакет — урок «Модули и импорт»; - что такое переменная окружения
PATH; - что команды урока — для POSIX-оболочки (bash или zsh на Linux и macOS); вариант для Windows дан рядом.
python -m venv,pyvenv.cfg,sys.prefixиsys.base_prefix;- что делает
activate,pip install,pip freeze,requirements.txt; - что изменилось в окружениях в 3.12, 3.13 и 3.14.
На чём проверено: CPython 3.11.15 и 3.12.3 (сборки Ubuntu), 3.13.7 и 3.14.7;
linux/x86_64, bash; pip — тот, что ставит в свежее окружение ensurepip
каждой версии. Документация и исходники сверены 14.10.2026. На эту дату вышел
и Python 3.15 (9 октября 2026) — урок на нём не запускался.
База: зачем окружение и как его создают
Полный пример — команды для bash/zsh (POSIX: Linux, macOS); их можно выполнить по порядку в пустой папке, для установки из PyPI нужна сеть:
python3 -m venv env # создать окружение в папке env
source env/bin/activate # активировать (bash/zsh)
python -m pip install requests==2.32.3 # поставить пакет в окружение
python -m pip freeze > requirements.txt # записать снимок установленного
deactivate # выйти из окружения
python3 -m venv env2 # создать второе, новое окружение
env2/bin/python -m pip install -r requirements.txt # поставить в него по снимкуpython -m pip, а не просто pip: так видно, каким интерпретатором — а
значит, в какое окружение — ставится пакет. Последняя команда ставит пакеты во
второе окружение без активации — по полному пути к его интерпретатору. На Windows папка скриптов
окружения — Scripts, а не bin; активация по документации venv —
env\Scripts\activate.bat в cmd.exe и env\Scripts\Activate.ps1 в
PowerShell. Прогоны урока — только Linux и bash.
Ответ уровня Junior на «зачем виртуальное окружение»: чтобы у каждого
проекта были свои версии пакетов, независимые от системы и других
проектов; создают python -m venv, активируют activate, ставят пакеты
pip, версии фиксируют в requirements.txt. Это верно, и за этим ответом
идут уточнения: что такое окружение физически, обязательно ли его
активировать, что именно делает activate и почему нельзя ставить пакеты в
системный Python. Механизмы ниже отвечают на них прогоном.
Механизм 1: окружение — это папка и pyvenv.cfg
Вывод bench/venv/basics.py, раздел 1, CPython 3.13.7:
1) что внутри папки окружения (верхний уровень и bin/)
['.gitignore', 'bin', 'include', 'lib', 'lib64', 'pyvenv.cfg']
bin/: ['Activate.ps1', 'pip', 'pip3', 'pip3.13', 'python', 'python3', 'python3.13'] + скрипты activate*
bin/python — символическая ссылка: True -> <python>/bin/python3.13
абсолютный путь окружения записан в bin/activate: True
первая строка bin/pip: #!<папка>/env/bin/python3.13
pyvenv.cfg:
home = <python>/bin
include-system-site-packages = false
version = 3.13.7
Внутри окружения нет копии стандартной библиотеки: bin/python — ссылка на
базовый интерпретатор, lib/…/site-packages — своя папка для пакетов (в
свежем окружении в ней только pip), lib64 — ссылка на lib, а
pyvenv.cfg записывает, откуда взят интерпретатор (home) и видны ли
системные пакеты (include-system-site-packages = false — не видны).
Зато в окружении записаны абсолютные пути: в bin/activate и в первой
строке bin/pip — по ней система выбирает, каким интерпретатором запустить
скрипт. Что это значит, показывает копия окружения в другую папку той же
машины. Вывод bench/venv/basics.py, раздел 6, CPython 3.13.7:
6) копия окружения в другую папку той же машины
moved/bin/python: sys.prefix -> <папка>/moved
moved/bin/pip --version -> <папка>/env/lib/python3.13/site-packages/pip
moved/bin/python -m pip --version -> <папка>/moved/lib/python3.13/site-packages/pip
без старой папки moved/bin/pip -> FileNotFoundError: No such file or directory
Интерпретатор копии видит новую папку, а bin/pip по шебангу запускает
интерпретатор старого окружения — и ставил бы пакеты туда. Без старой папки
он не запускается вовсе. python -m pip работает с копией, но остальные
скрипты из bin/ сломаны так же, как pip. На другой машине вдобавок home
и ссылка bin/python указывают на установку Python, которой там может не
быть. Поэтому окружение не переносят копированием: его пересоздают и ставят
зависимости заново.
Механизм 2: как интерпретатор понимает, что он в окружении
If a file named "pyvenv.cfg" exists one directory above sys.executable,
sys.prefix and sys.exec_prefix are set to that directory and it is also
checked for site-packages (sys.base_prefix and sys.base_exec_prefix will
always be the "real" prefixes of the Python installation).
Если на один каталог выше sys.executable лежит файл pyvenv.cfg, sys.prefix и sys.exec_prefix указывают на этот каталог, и в нём тоже ищется site-packages; sys.base_prefix и sys.base_exec_prefix всегда остаются «настоящими» префиксами установки Python.
Интерпретатор окружения запущен по пути, без активации. Вывод
bench/venv/basics.py, раздел 2, CPython 3.13.7:
2) интерпретатор окружения, без активации
sys.prefix -> <папка>/env
sys.base_prefix -> <python>
prefix != base_prefix -> True
куда ставятся пакеты -> <папка>/env/lib/python3.13/site-packages
sys.prefix указывает на окружение, sys.base_prefix — на базовую
установку, и пакеты ищутся и ставятся в site-packages окружения. Всё это
решает сам интерпретатор по файлу pyvenv.cfg на уровень выше себя — в
корне окружения. Отсюда
практическое следствие: в cron, Dockerfile или systemd окружение не
активируют — пишут полный путь env/bin/python script.py. Документация venv
говорит то же:
You don’t specifically need to activate a virtual environment, as you can just specify the full path to that environment’s Python interpreter when invoking Python
(активировать окружение не обязательно: можно просто указать полный путь к интерпретатору окружения при запуске Python).
А проверка «работаю ли я в окружении» из кода — sys.prefix != sys.base_prefix.
Кто именно ставит sys.prefix, зависит от версии: до 3.13 включительно — модуль
site при старте интерпретатора, с 3.14 — инициализация путей, ещё до
site. На обычный запуск это не влияет — раздел 2 прогона одинаков на
3.11–3.14. Разница видна только с ключом -S, который отключает site, — об
этом в разделе «Глубже».
Механизм 3: изоляция пакетов и pip freeze
Вывод bench/venv/basics.py, разделы 3 и 4, CPython 3.13.7:
3) pip list в свежем окружении
pip==25.2
4) изоляция: pip install tomli-w==1.0.0 в окружение
интерпретатор окружения -> импортирован, версия 1.0.0
базовый интерпретатор -> ModuleNotFoundError: No module named 'tomli_w'
у базового sys.prefix -> <python>
у базового пакеты в -> <python>/lib/python3.13/site-packages
Name в метаданных пакета -> tomli_w
pip freeze -> ['tomli_w==1.0.0']
В свежем окружении на 3.13 — только pip. Пакет, поставленный pip install
в окружение, интерпретатор окружения импортирует, а базовый — нет: пакеты
лежат в разных site-packages: у окружения — <папка>/env/lib/…, у
базового — своя. Это и есть изоляция: проекты с разными
версиями одной библиотеки живут на одной машине, не мешая друг другу и
системе.
pip freeze печатает снимок установленного — в формате requirements, как
сказано в описании команды:
Output installed packages in requirements format
(выводит установленные пакеты в формате requirements).
Пакет из PyPI попадает в снимок строкой имя==версия, но так выглядят не все
строки. В разделе 7 прогона в окружение дополнительно поставлены пакет из
локальной папки и editable-пакет (pip install -e — установка ссылкой на
папку с исходниками). Вывод bench/venv/basics.py, раздел 7, CPython 3.13.7:
7) pip freeze: что попадает в снимок
python -m pip freeze:
# Editable install with no version control (editpkg==0.1)
-e <папка>/editpkg
localpkg @ file://<папка>/localpkg
tomli_w==1.0.0
только в python -m pip freeze --all: ['pip==25.2']
Пакет из локальной папки записан прямой ссылкой (имя @ file://…),
editable-пакет — строкой -e с путём; эти пути указывают на папки этой
машины. Сам pip freeze пропускает, а --all его показывает. Что
пропускать, решает pip по версии Python: до 3.11 включительно — pip,
setuptools, distribute и wheel, с 3.12 — только pip
(pip/_internal/commands/freeze.py, строки 12–20 в pip 24.0 из 3.11.15). В
прогоне на 3.11 --all добавил pip==24.0 и setuptools==79.0.1.
Вывод freeze сохраняют в requirements.txt, чтобы
python -m pip install -r requirements.txt в новом окружении поставил те же
версии. Это сработает, если эти версии доступны для той платформы и версии
Python; побайтно одинаковой сборки снимок не обещает — он хранит имена,
версии и пути, а не сами файлы пакетов. pip сам отделяет freeze от
lock-файла:
pip freeze reports what is installed; it does not compute a lockfile or a solver result
(pip freeze сообщает, что установлено; lock-файл или результат разрешения зависимостей он не вычисляет).
Имя freeze печатает так, как оно записано в метаданных пакета, — там
Name: tomli_w, — а не так, как его набрали в pip install tomli-w.
Механизм 4: что делает activate
Вывод bench/venv/basics.py, раздел 5, CPython 3.13.7, bash на Linux:
5) активация в bash: что она меняет
VIRTUAL_ENV=<папка>/env
VIRTUAL_ENV_PROMPT=env
первый элемент PATH: <папка>/env/bin
python в PATH: <папка>/env/bin/python
после deactivate PATH прежний: да
activate — скрипт оболочки, а не часть Python. Для того, какой python
найдётся, важен один PATH: env/bin встаёт в нём первым, и команды
python и pip находят интерпретатор окружения. Кроме того, activate
задаёт VIRTUAL_ENV и VIRTUAL_ENV_PROMPT, добавляет имя окружения в
начало приглашения оболочки (PS1) и снимает PYTHONHOME, если она задана;
deactivate всё это возвращает. Внутри интерпретатора активация ничего не
меняет: если PYTHONHOME не задана, интерпретатор окружения работает
одинаково, запущен ли он через PATH или по полному пути.
Это POSIX-вариант для bash/zsh; для fish и csh в bin/ лежат свои скрипты
(activate.fish, activate.csh), а на Windows по документации venv —
env\Scripts\activate.bat в cmd.exe и env\Scripts\Activate.ps1 в
PowerShell. Прогон урока проверял только bash на Linux.
Глубже: что изменилось в окружениях
Сравнение прогонов на четырёх версиях показывает четыре изменения:
- 3.12: в свежем окружении нет
setuptools. На 3.11pip listв новом окружении —pipиsetuptools, с 3.12 — толькоpip. Свежее окружение наполняетensurepip, и набор задан в нём: на 3.11_PACKAGE_NAMES = ('setuptools', 'pip'), на 3.12 —('pip',). Проект, который неявно опирался наsetuptoolsв окружении, на 3.12 должен объявить его зависимостью явно. - 3.13:
python -m venvкладёт в окружение.gitignore. Его содержимоеcause the entire environment directory to be ignored by git
(заставляет git игнорировать весь каталог окружения) — окружение не попадёт в репозиторий, даже если его забыли вписать в.gitignoreпроекта. Файл создаёт командаpython -m venv, флаг--without-scm-ignore-filesэто отключает;venv.create()из кода по умолчанию его не создаёт. - 3.14:
sys.prefixокружения ставит инициализация путей, а не модульsite, и ключ-Sего больше не сбрасывает. Подробно — ниже, после списка. - 3.14: в
bin/есть ещё одно имя интерпретатора —𝜋thon. Его добавляют только на 3.14 и только при кодировке файловой системы UTF-8; на работу окружения оно не влияет.
Версия pip в свежем окружении своя у каждого интерпретатора: в прогонах —
24.0 на 3.11 и 3.12, 25.2 на 3.13, 26.2.1 на 3.14. Её обновляют внутри
окружения: python -m pip install --upgrade pip — или сразу при создании:
python -m venv --upgrade-deps env. Для этого флага и нужен список
CORE_VENV_DEPS в исходниках venv: на 3.11 — pip и setuptools, с
3.12 — pip.
Теперь о префиксе на 3.14. В документации site 3.14:
site is no longer responsible for updating sys.prefix and sys.exec_prefix on Virtual Environments. This is now done during the path initialization
(site больше не отвечает за то, чтобы выставить sys.prefix и sys.exec_prefix в виртуальных окружениях. Теперь это делается при инициализации путей)
— и поэтому префиксы окружения больше не зависят от -S, ключа, который
запускает интерпретатор без модуля site. Раздел 8 прогона — тот же
env/bin/python, но с -S. Вывод bench/venv/basics.py, раздел 8,
CPython 3.13.7:
8) env/bin/python -S: модуль site не импортируется
sys.prefix -> <python>
sys.base_prefix -> <python>
prefix != base_prefix -> False
site в sys.modules -> False
site-packages в sys.path -> False
На 3.11 и 3.12 — то же: префикс окружения ставил site, и без него
интерпретатор окружения считал себя базовой установкой. Тот же раздел на
CPython 3.14.7:
8) env/bin/python -S: модуль site не импортируется
sys.prefix -> <папка>/env
sys.base_prefix -> <python>
prefix != base_prefix -> True
site в sys.modules -> False
site-packages в sys.path -> False
Установка префикса и подключение site-packages теперь разделены: префикс
ставит инициализация путей, а site-packages окружения в sys.path
добавляет по-прежнему site, и при -S — последняя строка — их нет в
sys.path ни на одной версии. Без -S разницы между
версиями нет: раздел 2 прогона одинаков на 3.11–3.14, и прежнее объяснение
«интерпретатор сам находит окружение по pyvenv.cfg» остаётся верным — на
3.14 меняется только то, какая часть интерпретатора это делает.
Как отвечать на собеседовании
Короткий ответ: виртуальное окружение — папка со своим интерпретатором и
своим site-packages; создаётся python -m venv. Интерпретатор окружения
находит его сам по pyvenv.cfg, а activate нужен оболочке — через PATH
команда python находит интерпретатор окружения. Пакеты
ставят python -m pip внутрь окружения, установленные версии записывают pip freeze в
requirements.txt — это снимок установленного, а не lock-файл. В системный
Python пакеты не ставят: окружение изолирует версии проектов.
Этого достаточно, чтобы ответить верно. Дальше — то, что добавляют, если собеседник копает.
Если интервьюер копает глубже
На «обязательно ли активировать окружение». «Нет. Интерпретатор
env/bin/python сам ставит sys.prefix на окружение по pyvenv.cfg. В моём
прогоне без активации sys.prefix != sys.base_prefix — True. В Docker и
cron так и запускают — по полному пути».
На «что делает activate». «Главное — ставит env/bin первым в PATH,
поэтому python и pip находят окружение. Ещё задаёт VIRTUAL_ENV и
VIRTUAL_ENV_PROMPT, меняет приглашение оболочки и снимает PYTHONHOME;
deactivate всё возвращает. Сам Python она не меняет — только переменные
оболочки, которые он унаследует».
На «можно ли скопировать окружение на другую машину». «Нет. В
bin/activate и в первой строке bin/pip записан абсолютный путь
окружения. В моём прогоне даже в другой папке bin/pip копии запустил
старое окружение, а без старой папки не запустился вовсе. На другой машине ещё и home в pyvenv.cfg и ссылка
bin/python указывают на установку Python, которой там может не быть.
Переносят список зависимостей — requirements.txt или
lock-файл — и создают окружение заново, на совместимой платформе и версии
Python».
На «чем pip freeze отличается от requirements.txt, написанного руками».
«pip freeze печатает снимок всего установленного в формате requirements, с
точными версиями, включая транзитивные зависимости, — кроме самого pip (на
Python до 3.11 — ещё setuptools, wheel и distribute); editable- и
локальные установки он пишет строками с путём. Руками обычно пишут только
прямые зависимости. И это не lock-файл: pip прямо пишет, что freeze сообщает,
что установлено, а lock-файл не вычисляет».
Дальше спросят
Чем venv отличается от poetry и uv?
venv только создаёт окружение. pip ставит пакеты и при установке сам
подбирает совместимые версии, но не ведёт список зависимостей проекта и по
умолчанию не пишет lock-файл. Poetry и uv делают это: ведут зависимости в
pyproject.toml, записывают точные версии всего дерева в lock-файл и
создают окружение сами. В основе у них то же окружение с pyvenv.cfg.
Зачем нужен --system-site-packages?
Он записывает в pyvenv.cfg include-system-site-packages = true, и
окружение видит пакеты базовой установки — например, тяжёлые системные
библиотеки, которые не хочется ставить заново. Изоляция при этом
ослабевает: в прогоне урока этот ключ — false, и базовые пакеты не видны.
Почему ставить пакеты в системный Python — плохо?
В Linux-дистрибутивах системный Python использует сама операционная
система, и её утилиты
рассчитывают на конкретные версии пакетов. Установка поверх может их
сломать, а два проекта с разными версиями одной библиотеки в одном
site-packages не уживутся. Окружение решает обе проблемы.
Частые заблуждения
Без activate окружение не работает
Интерпретатор env/bin/python сам находит окружение по pyvenv.cfg: в прогоне без активации sys.prefix != sys.base_prefix — True.
Окружение содержит копию Python
У python -m venv на Linux и macOS bin/python — ссылка на базовый интерпретатор, а home в pyvenv.cfg указывает на его папку. С ключом --copies копируется сам исполняемый файл, а на Windows в окружение кладётся небольшой лаунчер, который запускает базовый python.exe. Стандартной библиотеки внутри нет ни в одном случае: она берётся из базовой установки.
activate что-то меняет внутри Python
Сам интерпретатор она не трогает: меняет переменные оболочки — PATH, VIRTUAL_ENV, VIRTUAL_ENV_PROMPT, приглашение. PYTHONHOME снимает, чтобы заданная переменная не увела интерпретатор окружения в чужую установку. После deactivate PATH прежний.
В новом окружении всегда есть setuptools
Только до 3.11 включительно. С 3.12 в свежем окружении один pip.
requirements.txt из pip freeze воспроизведёт окружение на любой машине
Это снимок установленного: имена, версии, а для editable- и локальных установок — пути этой машины. Те же версии поставятся, если они доступны для той платформы и версии Python; побайтной копии снимок не обещает, и lock-файлом, по словам самого pip, он не является.
Окружение можно скопировать в другую папку или на другую машину
В bin/activate и первой строке bin/pip — абсолютный путь окружения: в прогоне bin/pip копии запустил старое окружение, а без старой папки не запустился. На другой машине вдобавок может не быть установки Python, на которую указывает home. Окружение пересоздают по requirements.txt.
История версий
| Версия | Изменение | Что это значит для кода |
|---|---|---|
| 3.11 | Опорная точка урока. В свежем окружении — pip и setuptools. | |
| 3.12 | venv больше не ставит setuptools: в свежем окружении один pip. Сборка, неявно опиравшаяся на setuptools, должна объявить его явно. | |
| 3.13 | Команда python -m venv создаёт в папке окружения .gitignore со звёздочкой — git не видит окружение. | |
| 3.14 | sys.prefix окружения ставит инициализация путей, а не модуль site: в прогоне env/bin/python -S на 3.14.7 видит окружение (prefix != base_prefix — True), на 3.11–3.13 — нет. Без -S всё как раньше. Ещё в bin/ появилось имя интерпретатора 𝜋thon, только при кодировке файловой системы UTF-8; на работу окружения оно не влияет. | |
| 3.15 | Вышел 9 октября 2026. Урок на нём не запускался: прогоны — CPython 3.11–3.14, сверено 14.10.2026. |
Практика
Две задачи. Сначала ответьте, потом сверьтесь с настоящим выводом: в обеих правильный ответ взят из прогона скрипта, а не назначен.
Практика · что напечатает
import sys
print(sys.prefix == sys.base_prefix)
print(sys.prefix.endswith("env"))Практика · оцените
Проверьте себя
Скрипт в cron запускается как /app/env/bin/python job.py, activate не вызывается. Какие пакеты он увидит?
Чем измерено
Наблюдения этого урока получены этими скриптами. Каждый открывается прямо отсюда — вместе с записью прогона: на чём запускали и что получилось.
Это не пересказ и не отдельный текст: всё ниже взято из самой статьи — её выжимка, заголовки разборов, колонка «на самом деле» и таблица версий. Поэтому разойтись со статьёй эти тезисы не могут.
Суть
- Виртуальное окружение — это отдельная папка со своим интерпретатором и своим местом для пакетов. Его создаёт
python -m venv env, а пакеты в него ставитpip. Так у каждого проекта свои версии зависимостей, и установка пакета для одного проекта не ломает другой. - Отсюда главное: окружение — это интерпретатор
env/bin/python, а не «включённый режим». В корне окружения, на уровень вышеbin/python, лежитpyvenv.cfg, и по этому файлу интерпретатор сам ставитsys.prefix— каталог, который он считает своей установкой и от которого ищет пакеты, — на окружение; в прогоне без всякой активации.activateнужен оболочке: он кладётenv/binпервым вPATH, чтобы командаpythonнашла нужный интерпретатор. - Дальше — то, что отличает понимание от пересказа. Пакет, поставленный в окружение, базовому интерпретатору не виден:
ModuleNotFoundError.pip freezeпечатает снимок установленного в формате requirements: пакет из PyPI — строкойимя==версия, editable- и локальная установка — строкой с путём; из снимка делаютrequirements.txt. По нему ставятся те же версии, если они доступны для той платформы и версии Python, — но это не lock-файл и не побайтная копия. В свежем окружении на 3.11 два пакета,pipиsetuptools, а с 3.12 — одинpip. С 3.13python -m venvкладёт в окружение.gitignore, и git не видит окружение. С 3.14sys.prefixокружения ставит инициализация путей, а не модульsite, и запуск с-Sего уже не сбрасывает.
На самом деле
- Интерпретатор
env/bin/pythonсам находит окружение поpyvenv.cfg: в прогоне без активацииsys.prefix != sys.base_prefix—True. - У
python -m venvна Linux и macOSbin/python— ссылка на базовый интерпретатор, аhomeвpyvenv.cfgуказывает на его папку. С ключом--copiesкопируется сам исполняемый файл, а на Windows в окружение кладётся небольшой лаунчер, который запускает базовыйpython.exe. Стандартной библиотеки внутри нет ни в одном случае: она берётся из базовой установки. - Сам интерпретатор она не трогает: меняет переменные оболочки —
PATH,VIRTUAL_ENV,VIRTUAL_ENV_PROMPT, приглашение.PYTHONHOMEснимает, чтобы заданная переменная не увела интерпретатор окружения в чужую установку. ПослеdeactivatePATHпрежний. - Только до 3.11 включительно. С 3.12 в свежем окружении один
pip. - Это снимок установленного: имена, версии, а для editable- и локальных установок — пути этой машины. Те же версии поставятся, если они доступны для той платформы и версии Python; побайтной копии снимок не обещает, и lock-файлом, по словам самого pip, он не является.
- В
bin/activateи первой строкеbin/pip— абсолютный путь окружения: в прогонеbin/pipкопии запустил старое окружение, а без старой папки не запустился. На другой машине вдобавок может не быть установки Python, на которую указываетhome. Окружение пересоздают поrequirements.txt.
По версиям
- 3.11
- Опорная точка урока. В свежем окружении —
pipиsetuptools.< - 3.12
venvбольше не ставитsetuptools: в свежем окружении одинpip. Сборка, неявно опиравшаяся наsetuptools, должна объявить его явно.<- 3.13
- Команда
python -m venvсоздаёт в папке окружения.gitignoreсо звёздочкой — git не видит окружение.< - 3.14
sys.prefixокружения ставит инициализация путей, а не модульsite: в прогонеenv/bin/python -Sна 3.14.7 видит окружение (prefix != base_prefix—True), на 3.11–3.13 — нет. Без-Sвсё как раньше. Ещё вbin/появилось имя интерпретатора𝜋thon, только при кодировке файловой системы UTF-8; на работу окружения оно не влияет.<- 3.15
- Вышел 9 октября 2026. Урок на нём не запускался: прогоны — CPython 3.11–3.14, сверено 14.10.2026.<
Что разобрано
- База: зачем окружение и как его создают
- Механизм 1: окружение — это папка и pyvenv.cfg
- Механизм 2: как интерпретатор понимает, что он в окружении
- Механизм 3: изоляция пакетов и pip freeze
- Механизм 4: что делает activate
- Глубже: что изменилось в окружениях
- Как отвечать на собеседовании
- Дальше спросят
- Частые заблуждения
- История версий
- Практика
- Проверьте себя
- Чем измерено
Источники и что читать дальше
7 ИСТОЧНИКОВ
- Модуль site — как интерпретатор находит окружениеОфициальная документация. Строка документации модуля site: «If a file named "pyvenv.cfg" exists one directory above sys.executable, sys.prefix and sys.exec_prefix are set to that directory and it is also checked for site-packages (sys.base_prefix and sys.base_exec_prefix will always be the "real" prefixes of the Python installation)» (Если на один каталог выше sys.executable лежит файл pyvenv.cfg, sys.prefix и sys.exec_prefix указывают на этот каталог, и в нём тоже ищется site-packages; sys.base_prefix и sys.base_exec_prefix всегда остаются „настоящими“ префиксами установки Python). Сверено вызовом site.__doc__ на 3.11.15, 3.12.3, 3.13.7 и 3.14.7.https://docs.python.org/3.13/library/site.html
- Lib/venv/__init__.py — что venv кладёт в окружениеИсходный код CPython. На 3.13.7: строки 444–446 — метод _setup_pip, который наполняет свежее окружение вызовом ensurepip; строка 17 —
CORE_VENV_DEPS, список пакетов для флага --upgrade-deps (метод upgrade_dependencies, строки 585–590; на 3.11.15 —('pip', 'setuptools'), с 3.12.3 —('pip',)). Строки 279–290 — метод create_git_ignore_file: «The contents of the file cause the entire environment directory to be ignored by git» (Содержимое файла заставляет git игнорировать весь каталог окружения); командная строка python -m venv включает его по умолчанию (строка 662,default=frozenset(['git'])), а у EnvBuilder и venv.create по умолчаниюscm_ignore_files=frozenset()(строки 51 и 595). Строки 185–190 — lib64 как символическая ссылка на lib на 64-битных POSIX-системах, кроме macOS. Строки 318–345 — на Windows вместо ссылки в окружение копируется лаунчер venvlauncher.exe под именем python.exe; строки 623–626 — у командной строки на Windows символические ссылки по умолчанию выключены. На 3.14.7 строки 318–319 —suffixes.append('𝜋thon'), только приsys.version_info[:2] == (3, 14)и кодировке файловой системы UTF-8.https://github.com/python/cpython/blob/v3.13.7/Lib/venv/__init__.py - Lib/ensurepip/__init__.py — что ставится в свежее окружениеИсходный код CPython. На 3.11.15 строки 12–14:
_PACKAGE_NAMES = ('setuptools', 'pip'),_SETUPTOOLS_VERSION = "79.0.1",_PIP_VERSION = "24.0". На 3.12.3 строки 40–41:_PACKAGE_NAMES = ('pip',),_PIP_VERSION = "24.0". На 3.13.7 и 3.14.7_PACKAGE_NAMESи_SETUPTOOLS_VERSIONнет, ставится только pip;_PIP_VERSION(строка 13) — "25.2" и "26.2.1". Сверено по установленным файлам этих версий; 3.11.15 и 3.12.3 — сборки Ubuntu, номера строк даны по ним и в исходном коде CPython могут быть другими.https://github.com/python/cpython/blob/v3.12.3/Lib/ensurepip/__init__.py - Lib/venv/scripts/common/activate — что делает активацияИсходный код CPython. Шаблон activate на 3.13.7: строки 57–58 —
export VIRTUAL_ENV_PROMPT; 60–66 — снимает PYTHONHOME, если она задана; 69–71 — добавляет имя окружения в начало PS1, если не задана VIRTUAL_ENV_DISABLE_PROMPT; 76 —hash -r; строки 4–34 — функция deactivate, которая всё это возвращает.https://github.com/python/cpython/blob/v3.13.7/Lib/venv/scripts/common/activate - Модуль site в 3.14 — префиксы окружения ставит инициализация путейОфициальная документация. Документация site 3.14: «Changed in version 3.14: site is no longer responsible for updating sys.prefix and sys.exec_prefix on Virtual Environments. This is now done during the path initialization. As a result, under Virtual Environments, sys.prefix and sys.exec_prefix no longer depend on the site initialization, and are therefore unaffected by -S» (Изменено в 3.14: site больше не отвечает за то, чтобы выставить sys.prefix и sys.exec_prefix в виртуальных окружениях. Теперь это делается при инициализации путей. Поэтому в виртуальных окружениях sys.prefix и sys.exec_prefix больше не зависят от инициализации site и не меняются от -S). В исходнике: функция venv() в Lib/site.py на 3.13.7, строка 628, —
sys.prefix = sys.exec_prefix = site_prefix(в сборках Ubuntu 3.11.15 и 3.12.3 — строки 554 и 555); на 3.14.7, строки 634–637, она только предупреждает, если sys.prefix не совпал с папкой окружения. Строка документации модуля site (site.__doc__) на 3.14.7 осталась прежней. Прогон —bench/venv/basics.py, раздел 8. Сверено 14.10.2026.https://docs.python.org/3.14/library/site.html - pip/_internal/commands/freeze.py — что pip freeze пропускаетИсходный код. В pip 24.0, который ставит ensurepip на 3.11.15: строка 27 — описание команды «Output installed packages in requirements format» (Выводит установленные пакеты в формате requirements); строки 12–20 —
_should_suppress_build_backends()возвращаетsys.version_info < (3, 12), и тогда к пропускаемомуpipдобавляютсяsetuptools,distributeиwheel; с Python 3.12 пропускается толькоpip. В pip 25.2 (3.13.7) и 26.2.1 (3.14.7) то же правило — строки 11–19. Флаг --all пропуск отключает. Документация pip о различии со lock-файлом: «pip freeze reports what is installed; it does not compute a lockfile or a solver result» (pip freeze сообщает, что установлено; lock-файл или результат разрешения зависимостей он не вычисляет). Сверено по установленным файлам и pip.pypa.io 14.10.2026.https://pip.pypa.io/en/stable/cli/pip_freeze/ - venv — команды активации на разных платформахОфициальная документация. Таблица документации venv 3.14: POSIX, bash/zsh —
source <venv>/bin/activate; fish —source <venv>/bin/activate.fish; csh/tcsh —source <venv>/bin/activate.csh; Windows, cmd.exe —<venv>\Scripts\activate.bat; PowerShell —<venv>\Scripts\Activate.ps1. И: «You don’t specifically need to activate a virtual environment, as you can just specify the full path to that environment’s Python interpreter when invoking Python» (Активировать окружение не обязательно: можно просто указать полный путь к интерпретатору окружения при запуске Python). Прогон урока — только bash на Linux. Сверено 14.10.2026.https://docs.python.org/3.14/library/venv.html