Why Python Virtual Environments Matter
Python packages are installed globally by default. This causes version conflicts when different projects need different package versions:
Project A: requires Django 4.2
Project B: requires Django 3.2
# Without virtual environments: you can only have one version installed
# The newer install overwrites the older one, breaking one of your projects
Virtual environments create isolated Python installations with their own packages — each project gets its own library versions without interfering with others.
venv — Python's Built-in Tool
# Create a virtual environment
python3 -m venv myenv
# Activate (Linux/macOS)
source myenv/bin/activate
# Activate (Windows)
myenv\Scripts\activate.bat # CMD
myenv\Scripts\Activate.ps1 # PowerShell
# You'll see (myenv) prefix in your shell prompt
(myenv) $ pip install requests
# Deactivate
deactivate
What happens when you activate?
which python # before: /usr/bin/python3
source myenv/bin/activate
which python # after: /path/to/myenv/bin/python
Activation adds the environment's bin/ directory to the front of PATH, so python and pip commands use the environment's copies.
Managing packages
# Install packages
pip install requests
pip install "django>=4.2,<5.0" # version constraints
pip install requests==2.31.0 # exact version
# Install from requirements file
pip install -r requirements.txt
# List installed packages
pip list
pip freeze # exact versions, suitable for requirements.txt
# Generate requirements.txt
pip freeze > requirements.txt
# Check for outdated packages
pip list --outdated
# Upgrade a package
pip install --upgrade requests
# Uninstall
pip uninstall requests
requirements.txt best practices
# requirements.txt — include version constraints, not exact pins
# for application libraries you control
requests>=2.28.0,<3.0.0
django>=4.2.0,<5.0.0
celery[redis]>=5.3.0
# requirements-dev.txt — tools you don't ship
pytest>=7.0.0
black>=23.0.0
mypy>=1.0.0
For production deployments where you want reproducibility:
# Generate a pinned lockfile
pip freeze > requirements-lock.txt
# The lockfile pins exact versions of all transitive dependencies
# Use this for deployment, not the loose requirements.txt
pyenv — Managing Multiple Python Versions
venv manages packages; pyenv manages Python versions themselves.
# Install pyenv (macOS)
brew install pyenv
# Install pyenv (Linux)
curl https://pyenv.run | bash
# List available Python versions
pyenv install --list | grep "3\.12"
# Install a specific version
pyenv install 3.12.3
pyenv install 3.11.8
# List installed versions
pyenv versions
# Set global default
pyenv global 3.12.3
# Set local version for a project (creates .python-version file)
cd myproject
pyenv local 3.11.8
python --version # 3.11.8 (overrides global)
# Set version for current shell session
pyenv shell 3.10.14
pyenv + venv workflow
# 1. Go to project
cd myproject
# 2. Set Python version
pyenv local 3.12.3
# 3. Create virtual environment with that Python
python -m venv .venv
# 4. Activate
source .venv/bin/activate
# 5. Install dependencies
pip install -r requirements.txt
Add to .gitignore
# .gitignore
.venv/
venv/
env/
__pycache__/
*.pyc
.python-version # commit this if you want to enforce Python version for team
pip-tools — Better Dependency Management
pip-tools separates abstract requirements from pinned lockfiles:
pip install pip-tools
# requirements.in — your direct dependencies (abstract)
cat > requirements.in << EOF
requests>=2.28.0
django>=4.2.0
EOF
# Compile to pinned lockfile (includes all transitive deps)
pip-compile requirements.in -o requirements.txt
# Install from lockfile
pip-sync requirements.txt
# Update all packages to latest compatible versions
pip-compile --upgrade requirements.in
This gives you the best of both worlds: readable direct dependencies in .in files, reproducible full lockfile in .txt.
conda — Scientific Computing and Data Science
conda is popular in data science because it manages non-Python dependencies too (C libraries, CUDA, etc.).
# Create environment
conda create -n myenv python=3.12
# Activate
conda activate myenv
# Install packages
conda install numpy pandas matplotlib
conda install -c conda-forge scikit-learn # from community channel
# Install with pip inside conda env (for packages not in conda channels)
pip install some-package
# Export environment
conda env export > environment.yml
# Create environment from file
conda env create -f environment.yml
# List environments
conda env list
# Remove environment
conda env remove -n myenv
environment.yml
name: data-analysis
channels:
- conda-forge
- defaults
dependencies:
- python=3.12
- numpy>=1.24
- pandas>=2.0
- matplotlib>=3.7
- pip:
- custom-package>=1.0.0 # packages only on PyPI
Poetry — Modern Dependency Management
Poetry combines environment management, dependency resolution, and packaging in one tool.
# Install Poetry
curl -sSL https://install.python-poetry.org | python3 -
# Create new project
poetry new myproject
cd myproject
# Add dependencies
poetry add requests
poetry add "django>=4.2,<5.0"
poetry add --dev pytest black mypy # dev dependencies
# Install dependencies (creates .venv automatically)
poetry install
# Run in environment
poetry run python script.py
poetry run pytest
# Activate shell
poetry shell
# Update dependencies
poetry update
# Show dependency tree
poetry show --tree
pyproject.toml (Poetry)
[tool.poetry]
name = "myproject"
version = "1.0.0"
[tool.poetry.dependencies]
python = "^3.12"
requests = "^2.28.0"
django = ">=4.2,<5.0"
[tool.poetry.dev-dependencies]
pytest = "^7.0"
black = "^23.0"
mypy = "^1.0"
Poetry generates a poetry.lock file — a complete lockfile you commit to version control for reproducible installs.
uv — The Modern Fast Alternative
uv (from Astral) is a new Rust-based Python package manager that's 10-100x faster than pip:
# Install uv
pip install uv
# or: curl -LsSf https://astral.sh/uv/install.sh | sh
# Create virtual environment
uv venv
# Install packages (much faster than pip)
uv pip install requests django
# Install from requirements.txt
uv pip install -r requirements.txt
# Use pyproject.toml workflow
uv add requests # adds to pyproject.toml and installs
uv sync # install all deps from lock file
Choosing the Right Tool
| Tool | Best For |
|---|---|
| venv + pip | Simple projects, maximum compatibility |
| pyenv + venv | Multiple Python versions, any project type |
| pip-tools | Teams needing reproducible builds without heavy tooling |
| Poetry | Modern workflow, packaging and publishing packages |
| conda | Data science, scientific computing, non-Python dependencies |
| uv | Speed-critical workflows, modern projects (rapidly maturing) |
Common Problems and Fixes
# "command not found: python" after activation
which python3 # use python3 instead
python3 -m venv .venv
# Packages installed but can't import
# Check you're in the right environment
which python # should point to .venv/bin/python
# pip installs to wrong location
# Never use sudo with pip in a virtual environment
# If you accidentally did: rm -rf the venv and start fresh
# requirements.txt not reproducible across platforms
# Use pip-tools or Poetry which generate cross-platform lockfiles
→ Explore Python configuration files and JSON data with the JSON Viewer.