Python for JS Devs · Fundamentals
npm to uvSet up a real Python project and run your first Python — using the tooling map you already have in your head.
You already know how a JS project comes together: node runs code, npm manages dependencies, nvm manages runtime versions, package.json declares everything, and a lockfile pins it. Python has historically split these across a pile of tools (python, pip, venv, pyenv, pipx…). uv collapses all of that into one fast tool — think of it as npm + nvm + npx in a single binary.
| JavaScript world | Python + uv world | What it does |
|---|---|---|
node app.js | uv run app.py | Run a file (in the project env) |
npm init | uv init | Scaffold a new project |
npm install x | uv add x | Add a dependency |
npm install | uv sync | Install everything from the lockfile |
npx cowsay | uvx cowsay | Run a tool without installing it |
nvm use 20 | uv python pin 3.13 | Pick the runtime version |
package.json | pyproject.toml | Project + dependency manifest |
package-lock.json | uv.lock | Exact pinned versions |
.venv/) is Python's answer to node_modules/: an isolated per-project folder holding that project's dependencies and its own Python interpreter. The twist vs. Node: in Node the isolation is automatic; in Python you historically had to "activate" the venv. With uv you never activate anything — uv run uses the project's .venv automatically, exactly like npm scripts see node_modules.
One command, no Python required first (uv can install Python for you):
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
Verify it's there:
uv --version
uv init hello-python
cd hello-python
uv python pin 3.13 # write .python-version, like an .nvmrc
Look at what uv scaffolded — every file has a JS analogue you already know:
hello-python/
├── pyproject.toml # package.json
├── .python-version # .nvmrc
├── README.md
└── main.py # your entry file
Open main.py and replace it with this. Notice how little is ceremony:
function greet(name) {
return `Hi, ${name}!`;
}
const user = "Karthik";
console.log(greet(user));
def greet(name):
return f"Hi, {name}!"
user = "Karthik"
print(greet(user))
Four differences to notice right now (we'll go deep on these next lesson):
const/let. You just name a variable and assign it.f"...{x}...") are Python's template literals. Same idea, different sigil.Now run it — no activation, no build step:
uv run main.py
# → Hi, Karthik!
Just like typing node with no args drops you into a JS shell, uv gives you a Python REPL wired to your project:
uv run python
>>> greet = lambda n: f"Hi, {n}!"
>>> greet("world")
'Hi, world!'
>>> exit()
uv run python and look. You'll use this constantly.
Run the four commands, get Hi, Karthik! to print via uv run main.py, then open the REPL and add a second function shout(msg) that returns the message uppercased (hint: try msg.upper() in the REPL). If it prints, you've set up and run a real Python project. That's the whole foundation.
Retrieval practice — answer from memory before clicking. Getting it slightly wrong is how it sticks.
uv add mirrors npm install <pkg>: it installs the package, updates pyproject.toml, and refreshes uv.lock..venv/ folder is most like which JS thing?node_modules role. With uv you never manually "activate" it.activate step, or want to know how uv differs from poetry/pip? Just ask in the chat.