python versions & venvs
a guide to managing multiple python versions and virtual environments

overview
Managing multiple Python versions cleanly is essential for working across different projects or teams. In this post, I will walk through how to install and manage Python versions with pyenv on Linux (or a Chromebook) and how to use a specific version with virtual environments.

managing multiple Python versions
When working across different projects, you may need to use multiple Python versions locally. There are several approaches: For this guide, we will use pyenv because it allows you to manage and switch between multiple Python versions without interfering with the system Python that the OS relies on.

The installation process requires only a few steps.
  1. First, install the required pyenv dependencies:
    
    sudo apt update
    sudo apt install -y build-essential libssl-dev zlib1g-dev \
    libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm \
    libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev
    
  2. Then install pyenv via curl:
    
    curl https://pyenv.run | bash
    
  3. You can now configure your shell (~/.bashrc or ~/.zshrc) so pyenv can manage multiple Python versions and virtual environments. Ensure you restart your terminal so the changes take effect.
    
    export PATH="$HOME/.pyenv/bin:$PATH"      # --> adds pyenv's executable directory to your shell's search path
    eval "$(pyenv init --path)"               # --> sets up your environment so that the correct Python version is used
    eval "$(pyenv virtualenv-init -)"         # --> enables pyenv's virtual environment management plugin
    
  4. Finally, install the desired Python version:
    
    pyenv install 3.12.12 # replace with desired version
    
  5. As a quick sanity check, you can verify which Python versions are installed locally:
    
    pyenv versions
    

And, voilà! In only a few steps we installed a new Python version with pyenv. Each version is isolated and completely separate from the system Python.

Now that we have multiple Python versions installed locally, let's take a look at how Python organizes its files on the machine.

python file structure
Before diving into virtual environments, it is useful to understand how Python organizes its system files and versions on a local machine. The diagram below refers to the Python file structure on a Chromebook.
 /                              
├── usr/
│   └── bin/
│       └── python3               <- system Python (DO NOT TOUCH)
│
├── home/
│   └── luisa/                   
│       ├── .bashrc             
│       ├── .profile
│       ├── .pyenv/             
│       │   ├── versions/
│       │   │   ├── 3.12.12/         <- newly installed Python version 3.12.12
│       │   │   │   ├── bin/
│       │   │   │   │   ├── python   <- Python 3.12.12 interpreter
│       │   │   │   │   └── pip
│       │   │   │   └── lib/    
│       │   │   └── 3.11.1/          <- another Python version
│       │   └── shims/          
│       └── projects/
│           └── my_project/
│               ├── .python-version   <- pyenv local version
│               ├── venv/             <- virtual environment folder
│               │   ├── bin/
│               │   │   ├── python    <- venv Python, based on pyenv version
│               │   │   └── pip
│               │   └── lib/          
│               ├── main.py
│               └── requirements.txt

As you can see, Python files are stored under different directories across the machine, and each file has a different scope and purpose.

system python
On most Linux systems, /usr/bin/python3 points to the system-managed Python interpreter that comes with the operating system. An interpreter is the program you run when you type python3 in the terminal. This file should never be touched, as modifying it could break system tools.


├── usr/
│   └── bin/
│       └── python3  # --> system-level python, DO NOT TOUCH
...


newly installed python versions
When you install other Python versions (via tools like pyenv, Anaconda, or manual builds), each installation includes its own python3 executable located in a different directory. By adjusting your PATH environment variable—or by using a version manager like pyenv—you control which python3 executable runs when you type python3 in your terminal.

From the tree diagram we can see that additional versions of Python are generally installed in the home directory at /home/user/.pyenv/versions/. So, the actual path to the interpreter for the newly installed version becomes ~/.pyenv/versions/3.12.12/bin/python.

├── home/
│   └── luisa/                  ← your home directory (cd ~)
│       ├── .bashrc             ← shell configuration
│       ├── .profile
│       ├── .pyenv/             ← pyenv installation
│       │   ├── versions/
│       │   │   ├── 3.12.12/    ← Python version 3.12.12
│       │   │   │   ├── bin/
│       │   │   │   │   ├── python
│       │   │   │   │   └── pip
│       │   │   │   └── lib/    ← standard library
│       │   │   └── 3.11.1/     ← another Python version
│       │   └── shims/          ← pyenv shims for version switching
...


virtual environments
Now that we understand how Python stores files and interpreters for different versions, how do we select one of the installed versions for our projects? This is where virtual environments (venvs for short) come into play.

A virtual environment (or venv) is a self-contained directory that contains:
  1. a copy of the Python interpreter (the version is specified in the .python-version file)
  2. its own lib folder where project-specific packages are installed.
Using a venv ensures that each project can manage its dependencies independently, preventing conflicts between projects that require different versions of the same library.

The steps to activate a specific version of Python in a project are quite straightforward.
  1. First, select a version in the active shell:
    
    # Pick Python version using pyenv
    pyenv shell 3.12.12
    
    
  2. Then create a virtual environment:
    
    # Create a virtual environment in the project folder
    python -m venv venv
    
    
  3. Finally, activate the virtual environment and install a version-specific package:
    
    
    # Activate the virtual environment (Linux/macOS)
    source venv/bin/activate
    
    # Activate the virtual environment (Windows PowerShell)
    .\venv\Scripts\Activate.ps1
    
    # Install a specific package version
    pip install Flask==2.3.2
    
    # Verify installation
    pip show Flask
    
    # Save environment to requirements.txt
    pip freeze > requirements.txt
    
    
With these steps, you now have a project-specific virtual environment that: By combining pyenv and venvs, you gain full control over your Python setup. Each project can safely use different Python versions and package versions without interfering with each other or the system Python.

recap
In this tutorial, we learned to install different versions of Python on the same machine, and apply a specific version to a project.

We learned that Python versions are the interpreters themselves, and each version is installed separately. They exist independently of any specific project and are stored in isolated directories, separate from the system Python.

Virtual environments are instead project-specific containers that include a copy of a chosen Python interpreter along with their own directory for installed packages. A virtual environment doesn’t install a new Python version; instead, it uses an existing one and creates an isolated workspace so that each project can manage its dependencies without affecting others.

I hope that with this tutorial you have expanded your Python toolbox, and built the confidence to manage different Python versions and select the right one for your personal projects.

Until next time!