Modules & Project Structure
Scripts are fun, but projects pay. Four ideas that turn a pile of files into something shippable.
▶ Watch this reelWhat you'll learn
- The import system
- Folder layout
- Relative vs absolute imports
- Entry points
Remember this
- Modules import once into namespaces; avoid
import *; follow np/pd aliases - Use the src layout + absolute imports; fix cycles by extracting a third module
- if __name__ == "__main__" separates library from script; use editable installs
Import system
- Every
.py= a module; folder +__init__.py= a package. import a.b→ bind namespace ·from a import b→ bind name ·import x as y.- Avoid
from x import *(pollutes namespace, breaks tooling). - Standard aliases:
np(numpy),pd(pandas),plt(matplotlib).
Folder layout (src layout)
myproject/
pyproject.toml
src/myproject/
__init__.py
services/users.py
tests/test_users.py
- src layout forces imports through the installed package.
Relative vs absolute
- ❌
from . import users— breaks on moves/direct execution. - ✅
from myproject.services import users— always. - Circular imports → extract shared code to a third module.
Entry points
__name__ == "__main__"→ True only when run directly.- Run during dev:
python -m myproject.cli. - Ship a command:
[project.scripts]in pyproject.toml. - Daily driver:
uv pip install -e .(editable install).
Code: Project skeleton + safe entry point
# layout:
# myproject/
# pyproject.toml
# src/myproject/__init__.py
# src/myproject/config.py
# src/myproject/services/__init__.py
# src/myproject/services/users.py
# tests/test_users.py
# --- src/myproject/services/users.py --------------------------
from __future__ import annotations
_users: dict[str, dict] = {}
def add(name: str) -> dict:
_users[name] = {"name": name}
return _users[name]
def count() -> int:
return len(_users)
# --- src/myproject/cli.py --------------------------------------
from myproject.services import users
def main() -> None:
users.add("ada")
print(f"{users.count()} users")
if __name__ == "__main__":
main() # runs only when executed directly,
# not when imported
# --- pyproject.toml --------------------------------------------
# [project.scripts]
# myproject = "myproject.cli:main"
# dev workflow:
# uv pip install -e . # editable install
# myproject # now a real command