Python for JS Devs · Fundamentals

Modules, packages & imports

Every .py file is a module. Imports look like ESM but resolve by a different rule you must understand before structuring a FastAPI app.

Lesson 08Time ~12 min Win Split code into two files and importPrereq Lesson 07

1 · Import syntax, mapped from ESM

JavaScript (ESM)
import { add, sub } from "./math.js";
import * as math from "./math.js";
import _ from "lodash";

export function add() {}
Python
from .math import add, sub
import math                # use as math.add(...)
import httpx               # third-party package

def add(): ...             # no `export` — see below
The big difference: no export 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.

2 · Packages = folders (the __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.

3 · The if __name__ == "__main__" idiom

You'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()
Common beginner error 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.

🎯 Your win

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.

Check yourself

Q1. How do you make a function importable from a module?
There's no export; any top-level name is importable. Underscore-prefix means "private by convention".
Q2. How are nested module paths written in imports?
Packages import with dots: from app.routers.users import router.
Q3. if __name__ == "__main__": guards code that should run:
It runs the block only when the file is the entry point, not when imported.
Primary source Python docs — Modules. Covers modules, packages, and the import system.
💬 Ask your teacher: absolute vs relative imports in a FastAPI project — which should I standardize on? (Short answer: absolute. Ask why.)