Python for JS Devs · Fundamentals
Every .py file is a module. Imports look like ESM but resolve by a different rule you must understand before structuring a FastAPI app.
import { add, sub } from "./math.js";
import * as math from "./math.js";
import _ from "lodash";
export function add() {}from .math import add, sub
import math # use as math.add(...)
import httpx # third-party package
def add(): ... # no `export` — see belowexport
Python has no explicit exports. Every top-level name in a module is importable. The convention for "private" is a leading underscore (_helper), and a module can list its public API in __all__, but nothing is truly hidden. Coming from ESM's explicit exports, just remember: define it at module top-level and it's exportable.
__init__.py question)A folder of modules is a package. Imports use dots, not slashes, and are rooted at your project, not relative file paths:
myapp/
├── __init__.py # marks a package (can be empty)
├── main.py
├── routers/
│ ├── __init__.py
│ └── users.py # import: from myapp.routers.users import router
└── core/
└── config.py # import: from myapp.core.config import settings
So routers/users.js-style paths become myapp.routers.users dotted module paths. This is exactly the layout you'll use for FastAPI in Lesson 16.
if __name__ == "__main__" idiomYou'll see this at the bottom of scripts. It means "only run this when the file is executed directly, not when imported" — the analogue of a JS module having top-level code vs. being imported.
def main():
print("running directly")
if __name__ == "__main__": # true only via `python file.py`
main()
ModuleNotFoundError / relative-import errors usually mean you ran a file inside a package directly instead of running it as a module from the project root. With uv, run modules via uv run -m myapp.main rather than uv run myapp/main.py when they use package-relative imports.
In your hello-python project, create mathutils.py with def add(a, b): return a + b. In main.py, write from mathutils import add and print add(2, 3). Run uv run main.py. Splitting code across files and importing cleanly is the win — it's the skeleton of every real project.
export; any top-level name is importable. Underscore-prefix means "private by convention".from app.routers.users import router.if __name__ == "__main__": guards code that should run: