Installing Python Properly - What Nobody Teaches You
Reading time: ~15 minutes | Level: Foundation → Engineering
Here is a question that reveals whether someone truly understands their environment:
which python
which python3
python --version
python3 --version
Run those four commands. Do they all point to the same interpreter? Do any of them point to /usr/bin/python? Do you know why each answer is what it is?
Most developers cannot fully answer that. And that ignorance causes environment bugs, dependency conflicts, and deployment failures - not because the code is wrong, but because the environment is wrong.
This lesson changes that.
What You Will Learn
- Why Python is an interpreter, not an application
- The difference between system Python and your Python - and why this matters
- How
PATHcontrols everything about which Python runs - Why you need
pyenvfor managing multiple Python versions - How virtual environments work at the filesystem level
- Why
pip install Xglobally is infrastructure malpractice - How to make your environment fully reproducible
- The top 6 installation mistakes that waste hours of debugging time
Prerequisites
- Access to a terminal (macOS, Linux) or PowerShell (Windows)
- Basic understanding that Python is a programming language
- No prior environment management experience needed
The Mental Model: Python Is an Interpreter
Before touching installation, build the right mental model.
When you write print("hello") and run it, something has to read that text and execute it. That something is the Python interpreter - a compiled executable that:
- Reads your source code
- Tokenizes and parses it into an AST
- Compiles it to bytecode
- Executes the bytecode via the Python Virtual Machine
"Installing Python" means installing this interpreter.
You can have multiple interpreters installed simultaneously - Python 3.9, 3.11, 3.12 - each completely separate, each with their own packages, each potentially invoked with different commands.
The interpreter you run determines everything: the language features available, the standard library version, and which packages are accessible.
Watch: Python Installation Done Right
:::info Video This video covers installing Python correctly on all major platforms, understanding the interpreter, and avoiding the common mistakes that trip up most developers. :::
Part 1 - System Python: Why You Must Not Touch It
On macOS and Linux, Python is already installed. Do not use it for development. Do not install packages into it.
Here is why:
On macOS, Apple controls the system Python. Modifying it can break macOS utilities.
On Ubuntu/Debian, apt manages system Python. Installing conflicting packages with pip into system Python causes package manager failures.
The rule is absolute: system Python is read-only infrastructure. Your code lives in isolated environments.
Verify you are not using system Python:
which python3
# Should NOT be /usr/bin/python3
# Should be something like /home/you/.pyenv/shims/python3
# or /usr/local/bin/python3
python3 -c "import sys; print(sys.executable)"
# Prints the full path to the interpreter being used
Part 2 - PATH: The Mechanism That Controls Everything
PATH is an environment variable - a list of directories the OS searches when you type a command.
echo $PATH
# /home/user/.pyenv/shims:/home/user/.local/bin:/usr/local/bin:/usr/bin:/bin
When you type python3, the OS searches directories left to right until it finds an executable named python3.
This is why which python3 tells you which interpreter wins. The one found first in PATH wins.
Diagnosing PATH Problems
# Which python wins?
which python3
# All python executables on your PATH
type -a python3
# Current PATH
echo $PATH
# From inside Python, where is this interpreter?
python3 -c "import sys; print(sys.executable)"
:::warning Common Mistake "Python works in the terminal but not in my IDE." This is almost always a PATH mismatch - the IDE is using a different Python than your terminal. Check which interpreter your IDE is configured to use. :::
Part 3 - pyenv: Managing Multiple Python Versions
pyenv is the professional tool for managing multiple Python versions on a single machine.
Why pyenv Exists
- Project A needs Python 3.9
- Project B needs Python 3.11
- Your system has 3.8
Without pyenv, you are constantly fighting. With pyenv, each project declares its version and gets it.
Installing pyenv
macOS:
# Install Homebrew first if needed
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install pyenv
brew install pyenv
# Add to shell config (~/.zshrc or ~/.bashrc)
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init -)"' >> ~/.zshrc
source ~/.zshrc
Linux:
curl https://pyenv.run | bash
# Add to ~/.bashrc or ~/.bash_profile
export PYENV_ROOT="$HOME/.pyenv"
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
source ~/.bashrc
Using pyenv
# See available Python versions
pyenv install --list | grep " 3\."
# Install a specific version
pyenv install 3.11.9
pyenv install 3.12.4
# List installed versions
pyenv versions
# Set global default
pyenv global 3.11.9
# Set version for current directory only (saved to .python-version file)
pyenv local 3.11.9
# Verify
python3 --version # 3.11.9
The .python-version file travels with your project and tells pyenv which Python to use in that directory:
cat .python-version
# 3.11.9
This is version pinning at the environment level. Anyone who clones your project and has pyenv installed gets the exact same Python version.
Part 4 \text{---} Virtual Environments: Isolation Is Not Optional
Even with the right Python version, if you install packages globally, you create conflicts between projects.
A virtual environment is an isolated Python installation \text{---} a directory containing:
myproject/
└── venv/
├── bin/
│ ├── python3 ← symlink to your pyenv Python
│ ├── pip3
│ └── activate
├── lib/
│ └── python3.11/
│ └── site-packages/
│ ├── requests/ ← installed here, not globally
│ └── numpy/
└── pyvenv.cfg
Every package installed with pip goes into venv/lib/ \text{---} completely separate from other projects.
Creating and Using a Virtual Environment
# Navigate to your project
cd myproject
# Create virtual environment (using pyenv's Python)
python3 -m venv venv
# Activate (macOS/Linux)
source venv/bin/activate
# Activate (Windows PowerShell)
venv\Scripts\Activate.ps1
# Your prompt now shows (venv)
(venv) $ python --version # Uses venv Python
(venv) $ which python # Points to venv/bin/python
# Install packages \text{---} goes into venv only
(venv) $ pip install requests numpy pandas
# Deactivate when done
(venv) $ deactivate
Why This Matters
Without virtual environments - a single global package space causes conflicts:
| Package | Version | Required by |
|---|---|---|
| requests | 2.25 | Project A |
| requests | 2.31 | Project B |
Only one version can be installed globally - CONFLICT.
With virtual environments - each project has its own isolated package space:
project_a/venv/ | project_b/venv/ |
|---|---|
| requests==2.25 | requests==2.31 |
| flask==2.0 | django==4.2 |
| numpy==1.21 | numpy==1.26 |
| Isolated - no conflict | Isolated - no conflict |
Never use sudo pip install. Never install packages globally. If you find yourself doing this, you have the wrong mental model for Python package management.
Watch: Virtual Environments Explained
Part 5 - pip: Package Manager Deep Dive
pip installs packages from PyPI (Python Package Index). Understanding what pip does matters for debugging.
pip Basics
# Install a package
pip install requests
# Install specific version
pip install requests==2.31.0
# Install minimum version
pip install "requests>=2.28.0"
# Upgrade a package
pip install --upgrade requests
# Uninstall
pip uninstall requests
# List installed packages
pip list
# Show package details
pip show requests
Verifying the Right pip
There can be multiple pip executables on your system. Always verify:
# Which pip?
which pip
# Should point inside your venv or pyenv environment
# Verify pip matches your Python
pip --version
# pip 24.0 from /path/to/venv/lib/python3.11/... (python 3.11)
# Safest form - uses the specific Python's pip
python -m pip install requests
The python -m pip form is explicit: it uses the pip associated with the Python interpreter you just invoked.
Freezing and Reproducing Environments
The professional workflow uses requirements files:
# Capture current environment state
pip freeze > requirements.txt
# Later, reproduce that exact environment
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
The requirements.txt looks like:
certifi==2024.2.2
charset-normalizer==3.3.2
idna==3.6
numpy==1.26.4
requests==2.31.0
urllib3==2.2.1
Every package pinned to an exact version. Reproducible across machines, across time, across teammates.
For modern projects, consider pyproject.toml with pip-tools or poetry for more sophisticated dependency management. But requirements.txt remains universal.
Part 6 - Verifying Your Installation
After setup, always verify:
import sys
print(f"Python: {sys.version}")
print(f"Executable: {sys.executable}")
print(f"Path: {sys.path}")
# Check you are in a virtual environment
import os
in_venv = sys.prefix != sys.base_prefix
print(f"In virtual environment: {in_venv}")
print(f"Venv path: {sys.prefix if in_venv else 'N/A'}")
# Verify a package is accessible
import importlib
packages_to_check = ['numpy', 'requests', 'pandas']
for pkg in packages_to_check:
spec = importlib.util.find_spec(pkg)
status = "OK" if spec else "NOT FOUND"
print(f"{pkg}: {status}")
Common Installation Mistakes - Top 6
Mistake 1: Using System Python
# Wrong: modifying system Python
sudo pip install requests
# Right: use a virtual environment
python3 -m venv venv && source venv/bin/activate && pip install requests
Mistake 2: Forgetting to Activate the Virtual Environment
# You think you're installing into venv but you're not
pip install requests # without activating venv first
# Symptoms:
# - Package installs but import fails in your IDE
# - "where did I install that?" confusion
# Fix: always check your prompt for (venv) prefix
# Or check: which python && which pip
Mistake 3: Committing the venv/ Directory to Git
# Wrong: committing thousands of package files
git add venv/
# Right: add to .gitignore
echo "venv/" >> .gitignore
echo ".venv/" >> .gitignore
echo ".python-version" >> .gitignore # optional, often committed
Commit requirements.txt (or pyproject.toml), not the environment itself.
Mistake 4: Mixing Python 2 and Python 3
On some systems, python means Python 2 and python3 means Python 3.
python --version # 2.7.18 ← old
python3 --version # 3.11.9 ← what you want
# Always use python3 explicitly
# Or set pyenv global to a 3.x version
Mistake 5: Not Pinning Versions
# requirements.txt - wrong (floating)
requests
numpy
# requirements.txt - right (pinned)
requests==2.31.0
numpy==1.26.4
Unpinned requirements mean your environment changes when packages release new versions. That breaks reproducibility and causes "but it worked on my machine" failures.
Mistake 6: Ignoring Interpreter Path in IDEs
VS Code, PyCharm, Jupyter - all need to be told which interpreter to use.
# VS Code: Ctrl+Shift+P → "Python: Select Interpreter"
# Choose the one inside your venv
# Verify from inside IDE terminal:
python -c "import sys; print(sys.executable)"
# Must match your venv path
AI/ML Real-World Connection
Python environment management is not just for web development. It is mission-critical in machine learning:
# ML environments have tight version constraints
# NumPy 1.24+ changed behavior of certain operations
# TensorFlow 2.x requires specific CUDA versions
# PyTorch has different builds for CPU vs GPU
# A requirements.txt for ML might look like:
# numpy==1.26.4
# pandas==2.2.1
# scikit-learn==1.4.1
# torch==2.2.1+cu121 # PyTorch with CUDA 12.1
# transformers==4.38.2
# Mismatched versions cause:
# - Silent numerical errors (different NumPy behavior)
# - Import failures (TF/PyTorch CUDA mismatches)
# - Deprecation crashes (scikit-learn API changes)
In production ML systems, environments are often containerized with Docker to guarantee reproducibility across training clusters and inference servers.
Interview Questions
Q1: What is the difference between system Python and a user-installed Python?
Answer: System Python is installed and managed by the operating system (macOS, Linux). It is used by OS tools and scripts and should not be modified. User-installed Python (via pyenv, Homebrew, or official installers) is separate and controlled by the developer. Always use user-installed Python for development work.
Q2: What does PATH do and why does it matter for Python?
Answer: PATH is an environment variable containing a list of directories the OS searches when you type a command. When you type python3, the OS finds the first python3 executable in the directories listed in PATH. Managing PATH order determines which interpreter wins, which is why pyenv puts its shims directory at the front of PATH.
Q3: What does a virtual environment actually contain?
Answer: A virtual environment is a directory with: a Python interpreter (or symlink to one), its own pip, and a site-packages directory for installed libraries. Activating a venv prepends the venv's bin/ directory to PATH, making that Python and pip take precedence. All packages installed go into the venv's site-packages, not globally.
Q4: Why is python -m pip install safer than pip install?
Answer: python -m pip explicitly runs the pip associated with the currently active Python interpreter. Plain pip can be ambiguous - on some systems, pip might point to a different Python's pip than python. Using python -m pip eliminates the ambiguity.
Q5: What is pip freeze and when should you use it?
Answer: pip freeze outputs all installed packages and their exact versions in name==version format. Running pip freeze > requirements.txt captures a complete snapshot of your environment. Use it to share environments with teammates, deploy to servers, or restore an environment later. It is the standard reproducibility tool.
Q6: What is pyenv and why use it over the official Python installer?
Answer: pyenv manages multiple Python versions on a single machine and allows switching between them per-directory (via .python-version files). Unlike the official installer, which installs a single global version, pyenv lets you run Python 3.9 for one project and 3.12 for another without conflicts. It also does not modify system Python.
Quick Reference Cheatsheet
| Task | Command |
|---|---|
| Install pyenv (macOS) | brew install pyenv |
| Install Python version | pyenv install 3.11.9 |
| Set global Python | pyenv global 3.11.9 |
| Set project Python | pyenv local 3.11.9 |
| Create virtual environment | python3 -m venv venv |
| Activate (macOS/Linux) | source venv/bin/activate |
| Activate (Windows) | venv\Scripts\Activate.ps1 |
| Deactivate | deactivate |
| Install package | pip install package_name |
| Save environment | pip freeze > requirements.txt |
| Restore environment | pip install -r requirements.txt |
| Which Python? | which python3 |
| Which interpreter exactly? | python -c "import sys; print(sys.executable)" |
| In a venv? | python -c "import sys; print(sys.prefix != sys.base_prefix)" |
Graded Practice Challenges
Level 1 - Predict the Output
Given this terminal session, what does the last command print?
cd /tmp
python3 -m venv testenv
source testenv/bin/activate
pip install requests==2.25.0
pip freeze | grep requests
Show Answer
Output: requests==2.25.0
pip freeze outputs exactly installed packages and their pinned versions. Since requests==2.25.0 was installed, that is what prints. This demonstrates version pinning behavior.
Level 2 - Debug the Environment
A developer reports: "I installed numpy but import numpy fails in Jupyter."
What are the three most likely causes and how would you diagnose each?
Show Answer
Cause 1: Jupyter is using a different Python/kernel than where numpy was installed.
Diagnosis: In Jupyter, run:
import sys
print(sys.executable)
Compare to where you installed numpy.
Cause 2: You installed numpy without activating the virtual environment.
Diagnosis: Check whether pip install numpy was run with (venv) prefix in terminal.
Cause 3: Jupyter kernel is not pointing to the venv.
Fix: Inside the venv, install the kernel:
pip install ipykernel
python -m ipykernel install --user --name=myenv
Then select "myenv" kernel in Jupyter.
Level 3 - Design Challenge
You are setting up a Python development environment for a team of 5 engineers who will work on an ML project requiring:
- Python 3.11 exactly
- NumPy 1.26 (specific for a CUDA build)
- PyTorch 2.1 (GPU version)
- The ability to onboard new teammates in under 10 minutes
Design the complete setup: what files you create, what commands are in the README, and how you guarantee reproducibility.
Show Answer
Setup design:
# .python-version (pyenv)
echo "3.11.9" > .python-version
# requirements.txt (pinned)
numpy==1.26.4
torch==2.1.2+cu121
scikit-learn==1.4.0
pandas==2.2.0
README setup section:
# Prerequisites: pyenv installed
# 1. Clone repo
git clone https://github.com/org/ml-project
# 2. pyenv auto-selects Python 3.11.9 (from .python-version)
cd ml-project
python --version # 3.11.9
# 3. Create and activate venv
python -m venv venv
source venv/bin/activate # or venv\Scripts\activate on Windows
# 4. Install pinned dependencies
pip install -r requirements.txt
# 5. Verify
python -c "import torch; print(torch.__version__)" # 2.1.2+cu121
Reproducibility guarantees:
.python-versionpins Python via pyenvrequirements.txtpins all packagesvenv/is in.gitignore- Onboarding is 4 commands, not 40 decisions
Key Takeaways
- Python is an interpreter - installing Python means installing an executable program that runs your code
- System Python is off-limits for development - it belongs to the OS
- PATH determines which Python runs when you type a command - manage it deliberately
- pyenv manages multiple Python versions cleanly, with per-project version pinning via
.python-version - Virtual environments are mandatory - one per project, never shared, never committed to git
pip freeze > requirements.txtis the standard reproducibility toolpython -m pipis safer than barepip- avoids interpreter ambiguity- A good environment is reproducible in minutes by any teammate on any machine
- ML projects have especially tight version requirements - environment discipline is not optional there
