Skip to content

Python Virtual Environments: Why You Need Them and How to Create Them

1. The Problem

I installed requests version 2.28.0 for my Project A. Then I started Project B, which needed requests 2.31.0. I ran pip install requests==2.31.0 and suddenly Project A broke:

terminal-output.txt
ImportError: cannot import name 'default_headers' from 'requests.adapters'

This is the classic dependency conflict problem. When you install packages globally, different projects compete for the same package versions. One project wins, others break.

2. Why This Happens

The root cause is simple: Python by default installs packages to a single global location. All projects share this same pool of packages.

dependency-conflict.txt
+------------------+
| System Python | <-- Single global package pool
+------------------+
|
v
+---------------------------+
| requests==2.31.0 | <-- Latest version wins
| flask==3.0.0 |
| numpy==1.24.0 |
| ... (100+ packages) |
+---------------------------+
|
+----+----+
| |
Project A Project B
(needs (needs
2.28.0) 2.31.0)
^ ^
| |
+---------+---------+
|
CONFLICT!

The consequences are predictable:

  • Version conflicts: Different projects need different versions of the same package
  • Deployment failures: Your code works locally but fails on another machine
  • Permission issues: Global installation may require admin privileges
  • Debugging nightmares: Which package caused the problem? Hard to tell when everything is mixed together

3. The Solution: Virtual Environments

A virtual environment is an isolated Python installation with its own package directory. Each project gets its own sandbox.

isolated-environments.txt
+------------------+
| System Python | <-- Clean, untouched
+------------------+
+------------------+ +------------------+
| Project A Env | | Project B Env |
+------------------+ +------------------+
| requests==2.28.0 | | requests==2.31.0 |
| flask==2.2.0 | | flask==3.0.0 |
| pandas==1.5.0 | | numpy==1.26.0 |
+------------------+ +------------------+
| |
v v
Project A Project B
(works!) (works!)

Each environment is completely independent. Project A’s requests 2.28.0 stays in its sandbox. Project B’s requests 2.31.0 stays in another. No conflicts.

4. Step-by-Step Setup

4.1 Create the Virtual Environment

create-venv.sh
python -m venv myproject_env

This creates a directory myproject_env containing a standalone Python installation.

4.2 Activate the Environment

On Unix/macOS:

activate-unix.sh
source myproject_env/bin/activate

On Windows:

activate-windows.sh
myproject_env\Scripts\activate

After activation, your shell prompt changes:

prompt-change.txt
# Before activation
$ python script.py
# After activation
(myproject_env) $ python script.py

The (myproject_env) prefix confirms you’re inside the virtual environment.

4.3 Verify Activation

I always double-check I’m in the right environment before installing anything:

verify-activation.sh
# Unix/macOS
which python
# Output: /path/to/myproject_env/bin/python
# Windows
where python
# Output: C:\path\to\myproject_env\Scripts\python.exe

If the path points to your virtual environment directory, you’re good.

4.4 Install Packages

install-packages.sh
pip install requests==2.31.0 flask==3.0.0 pandas

All packages install into the virtual environment, not globally.

4.5 Save Dependencies

freeze-requirements.sh
pip freeze > requirements.txt

This creates a requirements.txt file with exact versions:

requirements.txt
requests==2.31.0
flask==3.0.0
pandas==2.0.3

4.6 Recreate Environment Later

On another machine, or after deleting the environment:

recreate-env.sh
python -m venv myproject_env
source myproject_env/bin/activate
pip install -r requirements.txt

Your environment is perfectly reproduced.

4.7 Deactivate

deactivate.sh
deactivate

You exit the virtual environment and return to system Python.

5. Common Mistakes I’ve Made

Mistake 1: Installing Before Activating

mistake1.sh
# WRONG - installs globally
pip install requests
# RIGHT - activate first
source myproject_env/bin/activate
pip install requests

I’ve done this many times. The package goes to global Python, and my project still fails with import errors.

Mistake 2: Not Pinning Versions

mistake2.sh
# WRONG - version could change
pip install requests
# RIGHT - pin the version
pip install requests==2.31.0

Without version pinning, pip freeze captures whatever version was current. Months later, pip install -r requirements.txt might get a different version if the lock file wasn’t generated properly.

Mistake 3: Using Legacy virtualenv

mistake3.sh
# OLD WAY (legacy)
virtualenv myenv
# NEW WAY (standard library, Python 3.3+)
python -m venv myenv

virtualenv is a third-party tool. venv is built into Python since 3.3. For new projects, use venv.

Mistake 4: Ignoring requirements.txt

I skipped pip freeze > requirements.txt on a project once. When my teammate tried to run it, they spent two hours figuring out which packages were needed.

6. How to Detect If You’re in a Virtual Environment

Sometimes I forget whether I’m in a venv. Here’s a quick Python check:

check_venv.py
import sys
def in_virtualenv():
return hasattr(sys, 'real_prefix') or (
hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix
)
if in_virtualenv():
print(f"Inside virtual environment: {sys.prefix}")
else:
print("Using system Python")

Running this tells you immediately where you stand.

7. venv vs virtualenv vs conda

tool-comparison.txt
| Feature | venv | virtualenv | conda |
|------------------|----------------|----------------|----------------|
| Built-in | Yes (Python 3.3+) | No (pip install) | No (separate) |
| Python versions | Same as system | Different versions | Different versions |
| Non-Python deps | No | No | Yes (C libs, etc) |
| Speed | Fast | Medium | Medium |
| Use case | Standard Python projects | Legacy projects | Scientific computing |

For most Python projects, venv is the right choice. It’s standard, lightweight, and built into Python.

8. Summary

Virtual environments solve the dependency conflict problem by isolating each project’s packages. The workflow is simple:

  1. python -m venv myenv - create
  2. source myenv/bin/activate - activate
  3. pip install packages - install
  4. pip freeze > requirements.txt - save
  5. deactivate - exit

This pattern is standard practice for all professional Python development. Skip it, and you’ll waste hours debugging mysterious import errors.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments