1.4.10 · D5Python & Scientific Computing

Question bank — Virtual environments and pip - conda

1,598 words7 min readBack to topic

Before we start, three words we lean on constantly, all built in the parent note:

  • A virtual environment is a self-contained folder holding its own Python interpreter and its own site-packages/ (the folder where installed packages live).
  • sys.path is the ordered list of folders Python searches to find a package — first match wins.
  • Activation just edits your shell's $PATH so the word python points at the env's interpreter instead of the system one.

True or false — justify

TF1. "Creating a virtual environment installs a fresh, separate copy of Python from the internet."
False. venv copies or symlinks the interpreter you already have; it never downloads a new Python. conda create python=3.9 is the exception — conda can fetch a different interpreter version.
TF2. "Activating an environment permanently changes my system so all future terminals use it."
False. Activation only edits the current shell's $PATH. Open a new terminal and you are back to the system Python until you activate again.
TF3. "If a package is missing from my activated env, my code will crash immediately."
False (and this is the danger). Python keeps searching sys.path; if it finds the package in the global site-packages/ later in the list, your code runs — masking the fact that your env is incomplete and unreproducible.
TF4. "pip freeze > requirements.txt records every package my project needs."
Only partly. It records everything installed in the active env, including transitive dependencies — but not packages you rely on from the global install because you forgot to activate first. Garbage in, garbage out.
TF5. "conda can install non-Python things like CUDA and MKL; pip cannot."
True. conda manages entire environments including compiled system libraries, while pip manages Python packages only — CUDA/MKL must be installed separately when you use pure pip.
TF6. "You must always choose either pip or conda for a project, never both."
False. A common real workflow is conda install for the heavy optimized binaries, then pip install for packages not in the conda channels. The rule of thumb is conda first, pip last, so pip doesn't overwrite conda's carefully-linked binaries.
TF7. "Two projects needing scikit-learn 0.24 and 1.3 can coexist without any isolation."
False. One global install means one version; installing 1.3 overwrites 0.24 and breaks the first project. That conflict is the entire reason virtual environments exist.
TF8. "pip install package without ==version is safe for reproducibility."
False. It grabs the latest version, which can change months later with breaking API changes. Pin exact versions (package==1.2.3) when reproducibility matters.
TF9. "Deleting the env folder cleanly uninstalls everything in it."
True. A venv is just a directory tree — its packages live inside it. Removing the folder removes the interpreter copy and all its packages, leaving the system untouched.

Spot the error

SE1. python3 -m venv env; pip install pandas; source env/bin/activate — what's wrong?
The order is backwards: pip install runs before activation, so pandas lands in the global site-packages/, not the env. Activate first, then install.
SE2. A colleague says "just add env/ to git so I can clone your exact environment."
Wrong approach. Env folders contain machine-specific paths and interpreter copies and don't transplant across OSes. Commit requirements.txt (or environment.yml) instead and let them recreate the env.
SE3. pip freeze > requirements.txt was run in a shell where the env was not activated. What's the bug?
It freezes the global package list, not the project's. The resulting file describes the wrong environment — recreating it elsewhere gives "ModuleNotFoundError" for things you actually used.
SE4. conda env export > environment.yml then hand-deleting the pip: section "to keep it clean."
You just dropped every package installed via pip. On recreation those are silently missing. The pip: block is not clutter — it is part of the environment's real dependency set.
SE5. pip install package_A==2.0 package_B==2.0 fails with a resolver conflict; the fix suggested is pip install --force-reinstall. Why is that wrong?
Force-reinstall ignores the dependency solver and can leave an env where A and B silently disagree on, say, the numpy version. The real fix is two separate environments or relaxing one constraint — not overriding the resolver.
SE6. Someone edits ~/.bashrc to export PATH="/system/bin:$PATH" after the conda init block "to be safe."
They put the system path first, so python now resolves to system Python even inside an "activated" env. Order in $PATH decides which interpreter wins; the env's bin/ must come first.

Why questions

WHY1. Why does activation prepend (not append) the env's folder to $PATH?
Because $PATH and sys.path are searched left-to-right, first match wins. Prepending guarantees the env's python and packages are found before the system ones — that is the whole mechanism of isolation.
WHY2. Why does pip install dependencies in topological order (dependencies before dependents)?
Because a package can't function until the things it imports already exist. If A needs B, installing B first means A finds it ready on import — installing out of order would leave broken references.
WHY3. Why are conda downloads usually larger than pip's?
conda ships pre-compiled binaries including bundled system libraries (like MKL), so nothing compiles on your machine. pip often downloads smaller wheels or source that lean on libraries already on your system.
WHY4. Why does pip install sometimes require compilation while conda "just works"?
pip may fetch a source distribution that must be built against your local compiler and system libraries; conda's channels host binaries already built for your OS/architecture, sidestepping the build step.
WHY5. Why does python -c "import sys; print(sys.executable)" prove isolation better than checking the shell prompt?
The (env) prompt is cosmetic and can lie if activation half-failed. sys.executable shows the actual interpreter path Python is running from — if it points inside your env folder, isolation is genuinely active.
WHY6. Why must every new terminal re-run activation?
Because activation only mutates that shell process's environment variables. A new shell starts from your default profile with the system Python, unaware of any env you activated elsewhere.

Edge cases

EC1. What happens if you pip install something while no env is activated at all?
It installs into the global site-packages/, often needing sudo/admin and polluting the system for every project — exactly the "dependency hell" venvs were built to avoid.
EC2. You activate env A, then activate env B without deactivating A. Which wins?
B. Its bin/ gets prepended in front, so python now points to B. Nesting is messy and error-prone — deactivate before switching to keep $PATH clean.
EC3. Package needed by project X requires numpy>=1.20, project Y requires numpy<1.20. Is one env enough?
No — a single env can hold only one numpy version, and no version satisfies both bounds simultaneously. The clean answer is two environments, one per constraint.
EC4. A requirements line reads just pandas (no version). You install today and again in a year. Same result?
Not guaranteed. Each install fetches whatever is latest at that moment, so the two environments can differ. Unpinned versions make "works on my machine" temporary.
EC5. You delete the env folder while it is still activated in your current shell. What breaks?
The shell's $PATH still points at the now-missing bin/, so python/pip calls fail until you deactivate (or open a fresh terminal). The system itself is unharmed — only that shell is confused.
EC6. Can you have Python 3.8 in one env and Python 3.11 in another on the same machine?
Yes with conda (conda create python=3.8 vs python=3.11) because conda installs the interpreter itself. Plain venv clones whatever Python you invoked it with, so mixing versions needs multiple base Pythons installed.
EC7. An env's site-packages/ is empty but import numpy still succeeds. Bug or feature?
A leak, not a feature. Python fell through to a later sys.path entry (usually global site-packages). Your env isn't truly isolated for numpy, and a pip freeze won't capture it.

Recall Quick self-test

One-line reasons only, then check yourself. Does activation download a new Python? ::: No — it only edits $PATH to point at an existing interpreter copy. Why does forgetting to activate silently "work"? ::: Python keeps searching sys.path and finds the package in the global folder. conda vs pip in one line? ::: conda manages whole environments incl. system libs; pip manages Python packages only. Fix for two mutually-incompatible numpy pins? ::: Two separate environments.

See also: 2.1.3-Setting-up-development-environment, 1.5.1-NumPy-arrays-and-vectorization, 1.4.8-File-IOand-data-serialization, 1.4.12-Profiling-and-optimization, 1.4.1-Python-basics-syntaxand-data-types.