Virtual Environments Beginner¶
When you'd use this
Isolate each project's dependencies so they never collide.
Create a per-project sandbox for installed packages — so Project A can use an old library version and Project B a new one, without either breaking the other or your system Python.
Why virtual environments exist¶
Without isolation, every pip install dumps packages into one shared location, so two projects that need different versions of the same library can't coexist.
Imagine you have two projects. One needs requests version 2.25, the other needs 2.31. If you install packages globally, the second install overwrites the first — and now one project is quietly broken. Multiply that across dozens of projects and you get "dependency hell."
A virtual environment is a self-contained folder holding its own Python interpreter link and its own site-packages directory. When it's active, pip install puts packages there, not system-wide. Each project gets its own clean, isolated set of dependencies.
Don't install into system Python
On many systems, pip install without an active environment either needs admin rights or can break OS tools that rely on specific package versions. Modern Python (3.11+) will often refuse with an "externally-managed-environment" error. A virtual environment is the correct fix — not --break-system-packages.
Creating and activating a venv¶
venv ships with Python — no install needed. Create once per project, then activate it in each new terminal session.
Python's built-in venv module is all you need to start.
Activating it differs by operating system:
# macOS / Linux
source .venv/bin/activate
# Windows (PowerShell)
.venv\Scripts\Activate.ps1
# Windows (cmd.exe)
.venv\Scripts\activate.bat
Once active, your shell prompt is prefixed with the environment name:
That (.venv) prefix is your signal that installs and runs now happen inside the sandbox. To leave it:
Why name it .venv?
The leading dot keeps it hidden in listings, and .venv is the convention most editors (including VS Code) auto-detect. Add it to .gitignore — you never commit the environment itself, only the list of what to install (see below).
Installing packages with pip¶
With the environment active, pip installs into it. Check what's installed with pip list.
# Install a package (goes into the active venv only)
pip install requests
# Install a specific version
pip install "requests==2.31.0"
# See what's installed
pip list
# Upgrade pip itself
python -m pip install --upgrade pip
A quick sanity check that isolation is working — run this inside an active venv:
import sys
# Inside an active venv, this points into your project's .venv folder,
# not the system Python installation.
print(sys.prefix)
Example output (macOS/Linux):
The path points into your project, confirming packages land in the sandbox rather than system-wide.
requirements.txt: sharing dependencies¶
Pin your project's dependencies to a text file so anyone (including future you, or CI) can recreate the exact environment.
You don't commit the .venv folder — you commit a list of what it should contain.
That produces a pinned list:
Anyone cloning your project then recreates the environment in two steps:
python -m venv .venv
source .venv/bin/activate # or the Windows equivalent
pip install -r requirements.txt
Keep a readable top-level list too
pip freeze captures everything, including sub-dependencies. Many projects also keep a short, human-edited requirements.txt listing only the packages they directly use (e.g. just requests and flask), and let pip resolve the rest. For reproducible deployments, the fully pinned version is safer.
Modern alternatives¶
venv + pip is the baseline every Python developer should know. Several tools wrap or replace it with extra convenience — worth knowing by name.
| Tool | What it adds | When to reach for it |
|---|---|---|
venv + pip | Nothing extra — the standard-library baseline | Always a safe default; zero to install |
uv | Extremely fast installs; creates venvs and resolves deps in one tool | Modern projects wanting speed |
poetry | Dependency resolution + lock file + packaging in one | Libraries you'll publish, or teams wanting reproducible locks |
pipenv | Combines pip + virtualenv with a Pipfile lock | Projects already standardized on it |
conda | Manages non-Python deps too (C libs, CUDA) | Data science / scientific stacks |
Start with venv + pip until the workflow is second nature. The alternatives solve real problems (speed, lock files, native dependencies), but they all build on the same core idea you just learned: an isolated, per-project set of packages.
Practice exercises¶
- Create a new project folder, make a
.venvinside it, activate it, and confirmsys.prefixpoints into that folder. - Install
requestsin the environment, runpip freeze > requirements.txt, then deactivate and inspect the file. - Delete the
.venvfolder entirely, recreate it, and restore your packages fromrequirements.txtin one command. - Add a
.gitignoreline that excludes.venv/but keepsrequirements.txttracked, and explain in a comment why you commit one but not the other. - Research the "externally-managed-environment" error: what causes it, and why is creating a venv the right fix rather than forcing a system install?
💬 Discussion
Have a question about this topic? Found an error? Share your thoughts below.