Python by Contrast
Tooling that finally makes sense — uv & virtualenvs
Lesson 2 of 5
What you'll learn
- Understand why virtual environments exist, contrasted with node_modules
- Drive a project with uv — init, add, run, sync
- Map pyproject.toml and uv.lock onto package.json and its lockfile
In Node, npm install drops dependencies into ./node_modules, so isolation is the default — two projects can pin different versions and never meet. Python is the other way around: pip install puts packages into the site-packages of an interpreter, shared by everything that runs on that interpreter. A virtual environment (venv) restores per-project isolation by giving the project its own lightweight interpreter with its own site-packages.
python -m venv .venv # the classic way: create the env...
source .venv/bin/activate # ...then activate it, so "python" means this project's
uv, the one-tool answer
Historically you juggled pip, venv, pip-tools, and pyenv. uv (from Astral, the ruff team) replaces the stack with one fast Rust binary that manages the venv for you — you never activate anything:
uv init myapp # scaffold pyproject.toml (like npm init)
uv add httpx # add a dep, update uv.lock, sync .venv (like npm install <pkg>)
uv run main.py # run inside the project's env, no activate step
uv sync # recreate the env exactly from the lockfile (like npm ci)
pyproject.toml is your package.json
One file declares the project, its Python floor, and its dependencies; uv.lock pins the full resolved tree, exactly like a lockfile in Node.
[project]
name = "myapp"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["httpx>=0.27"]
{
"name": "myapp",
"version": "0.1.0",
"engines": { "node": ">=22" },
"dependencies": { "undici": "^6.0.0" }
}
Don't pip install into the system Python
Modern macOS and Linux mark their bundled interpreter as externally managed, and a bare pip install will refuse to run — correctly. Every project gets its own environment; with uv that isolation costs you nothing.
The challenge is a JS model of environment resolution: a shared global site-packages versus two per-project venvs holding conflicting pins side by side.
Run it. This models Python package lookup: each venv resolves its own pinned version, which one shared site-packages could never do.
Why do Python projects need virtual environments when Node projects don't?
Next: Python's core containers, comprehensions, and the dunder methods that power the whole language.
Saved on this device. Sign in to sync your progress everywhere.