How to Use Python Virtual Environments
A Python virtual environment is an isolated Python installation that maintains its own packages, dependencies, and Python binaries separate from your system's global Python installation. Without...
Key Insights
- Virtual environments isolate project dependencies to prevent version conflicts between projects and protect your system Python installation from corruption
- The built-in
venvmodule is sufficient for most use cases—you don’t need third-party tools unless you have specific requirements like Python 2 support - Always commit
requirements.txtto version control but never commit the virtual environment directory itself
What Are Virtual Environments and Why Use Them?
A Python virtual environment is an isolated Python installation that maintains its own packages, dependencies, and Python binaries separate from your system’s global Python installation. Without virtual environments, you’re installing all packages globally, which creates a dependency management nightmare.
Here’s the fundamental problem: imagine you’re maintaining two Django projects. Project A runs on Django 2.2 (legacy code you’re gradually migrating), while Project B uses Django 4.2. If you install packages globally, you can only have one Django version installed at a time:
# Global installation creates conflicts
pip install Django==2.2.0 # Project A needs this
pip install Django==4.2.0 # This overwrites the previous version
# Now Project A breaks because it's incompatible with Django 4.2
Virtual environments solve three critical problems:
Dependency isolation: Each project gets its own package versions without conflicts. Your Django 2.2 project and Django 4.2 project coexist peacefully.
Reproducibility: You can recreate the exact environment on another machine or for another developer. This eliminates “works on my machine” problems.
System protection: Installing experimental or poorly-maintained packages won’t corrupt your system Python installation. This is especially important on macOS and Linux where system tools depend on Python.
Creating Virtual Environments with venv
Python 3.3+ includes venv as a standard library module. You don’t need to install anything extra. The basic syntax is straightforward:
python -m venv myenv
This creates a directory called myenv containing a complete Python environment. Let’s look at what gets created:
python -m venv .venv
# Directory structure created:
# .venv/
# ├── bin/ (Scripts/ on Windows)
# │ ├── activate
# │ ├── pip
# │ └── python
# ├── include/
# ├── lib/
# │ └── python3.x/
# │ └── site-packages/
# └── pyvenv.cfg
The naming convention .venv is widely adopted because the leading dot hides the directory on Unix systems and most .gitignore templates already exclude it. Some developers prefer venv or env, but consistency matters more than the specific name you choose.
You can also specify which Python version to use when creating the environment:
# Use a specific Python version
python3.11 -m venv .venv
# On Windows with multiple Python versions
py -3.11 -m venv .venv
The pyvenv.cfg file contains metadata about the environment, including the path to the base Python installation. The site-packages directory is where pip will install packages—this is your isolated dependency space.
Activating and Deactivating Virtual Environments
Creating a virtual environment doesn’t automatically use it. You must activate it, which modifies your shell’s environment variables to prioritize the virtual environment’s Python and packages.
On macOS and Linux:
source .venv/bin/activate
# Your prompt changes to indicate the active environment:
(.venv) user@machine:~/project$
On Windows (Command Prompt):
.venv\Scripts\activate.bat
# PowerShell requires a different script:
.venv\Scripts\Activate.ps1
If you get an execution policy error on PowerShell, you may need to run:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Once activated, verify you’re using the virtual environment’s Python:
which python # macOS/Linux
# Output: /path/to/project/.venv/bin/python
where python # Windows
# Output: C:\path\to\project\.venv\Scripts\python.exe
python --version
# Verify this matches your expected version
To deactivate and return to your system Python:
deactivate
Your prompt returns to normal, and python commands now reference your system installation again. You can activate and deactivate as many times as needed—there’s no limit.
Managing Packages in Virtual Environments
With your virtual environment activated, pip installs packages into the isolated site-packages directory. This is where the magic happens:
# Activate first
source .venv/bin/activate
# Install packages
pip install requests
pip install flask pandas numpy
# See what's installed
pip list
# Output shows only packages in this environment:
# Package Version
# ---------- -------
# Flask 3.0.0
# requests 2.31.0
# ...
The killer feature for reproducibility is freezing your dependencies:
pip freeze > requirements.txt
This creates a file listing every package and its exact version:
certifi==2023.11.17
charset-normalizer==3.3.2
click==8.1.7
Flask==3.0.0
idna==3.6
requests==2.31.0
urllib3==2.1.0
Another developer (or you on another machine) can recreate the exact environment:
# Create and activate a new virtual environment
python -m venv .venv
source .venv/bin/activate
# Install all dependencies
pip install -r requirements.txt
This installs the precise versions listed, ensuring consistency across environments. Update your requirements.txt whenever you add or upgrade packages:
pip install new-package
pip freeze > requirements.txt
git add requirements.txt
git commit -m "Add new-package dependency"
Alternative Tools: virtualenv and virtualenvwrapper
While venv handles most use cases, alternative tools exist for specific scenarios.
virtualenv is a third-party package that predates venv and offers more features:
pip install virtualenv
# Create environment (works with Python 2 and 3)
virtualenv myenv
# Specify Python version explicitly
virtualenv -p python3.11 myenv
# Create without pip (faster for temporary environments)
virtualenv --no-pip myenv
Use virtualenv if you need Python 2 support (though you really shouldn’t be using Python 2 anymore) or want faster environment creation for CI/CD pipelines.
virtualenvwrapper adds convenience commands for managing multiple virtual environments:
pip install virtualenvwrapper
# Create and activate in one command
mkvirtualenv myproject
# Switch between environments
workon myproject
workon otherproject
# Delete an environment
rmvirtualenv oldproject
These tools shine when you’re juggling many projects, but they add complexity. For most developers, venv is sufficient and has the advantage of being built-in.
Best Practices and Common Workflows
Never commit virtual environments to version control. They’re large, platform-specific, and completely reproducible from requirements.txt. Add this to your .gitignore:
# Virtual environments
venv/
.venv/
env/
ENV/
Separate development and production dependencies. Create two requirements files:
# requirements.txt - production dependencies only
Flask==3.0.0
requests==2.31.0
psycopg2-binary==2.9.9
# requirements-dev.txt - includes dev tools
-r requirements.txt
pytest==7.4.3
black==23.12.1
flake8==6.1.0
Install for development:
pip install -r requirements-dev.txt
Integrate with your IDE. Modern IDEs detect virtual environments automatically, but you can configure them explicitly:
- VS Code: Select the Python interpreter from
.venv/bin/pythonusing the command palette (Cmd/Ctrl+Shift+P → “Python: Select Interpreter”) - PyCharm: Settings → Project → Python Interpreter → Add Interpreter → Existing Environment
Automate activation with direnv. For Unix systems, direnv automatically activates virtual environments when you enter a project directory. Create .envrc:
source .venv/bin/activate
Pin your dependencies. Using exact versions (==) in requirements.txt ensures reproducibility but prevents security updates. Consider using pip-tools to manage this:
pip install pip-tools
# requirements.in - loose constraints
Flask>=3.0.0
requests>=2.31.0
# Generate pinned requirements.txt
pip-compile requirements.in
Virtual environments are non-negotiable for professional Python development. They prevent dependency hell, ensure reproducibility, and protect your system Python. Use venv for simplicity, commit your requirements.txt, and never commit the environment directory itself. Your future self—and your teammates—will thank you.