Python Fundamentals for the Web
Reinforce Python essentials tailored to backend development and reliable, readable code.
Content
Modules and packages
Versions:
Watch & Learn
AI-discovered learning video
Sign in to watch the learning video for this topic.
Python Modules and Packages for the Web — Organize Your Code Like a Pro
"If functions are the verbs of your program and data structures the nouns, then modules are the filing cabinets where your code stops being a hot mess and starts being maintainable." — probably a slightly too-enthusiastic TA
You’ve already learned about data types and structures and how functions and scope help you build logic. Now imagine a real web app: dozens of routes, templates, utilities, and static assets (hello again, CSS3 layouts and responsive design). Without organization, your project will quickly look like a closet where you shove everything — socks, a router, and two mysterious JavaScript files you don’t remember writing.
Modules and packages are Python’s way of giving your project closets with labeled drawers. They let you split code into focused files, create namespaces, reuse code, and manage dependencies — which is essential when building web apps that scale.
What is a module? What is a package?
- Module: Any Python file (example: utils.py) that defines functions, classes, and variables. Import it and use what's inside.
- Package: A directory containing an
__init__.pyfile (even if empty) that groups multiple modules and subpackages into a single namespace.
Why this matters for web dev
- Keep route handlers, database models, form validators, and utilities separated.
- Avoid name collisions (two
utils.pyfiles living in different packages are fine). - Make testing and reuse straightforward.
- Easy to install and share with pip when a piece grows into a library.
How imports work (quick, practical refresher)
Three common styles:
# import the module
import math
# import specific names
from math import ceil, floor
# import with alias
import numpy as np
Key details:
- Imports create a module object and cache it in
sys.modules(fast subsequent imports). from module import namecopies references into your namespace — changes in the original module's name won't automatically update your local name.- Use aliases to avoid verbose names or collisions (
import mypackage.long_mod_name as lm).
Pro tip: Overusing
from module import *is asking for trouble. It pollutes your namespace and makes debugging import-related bugs harder.
Package layout: a typical small web app
Imagine a tiny Flask app. File structure:
myapp/
run.py
requirements.txt
myapp/
__init__.py
routes.py
models.py
utils/
__init__.py
auth.py
validators.py
static/
css/
js/
templates/
base.html
index.html
Example myapp/__init__.py (creating the Flask app):
from flask import Flask
app = Flask(__name__)
# import routes so they register with the app
from . import routes
routes.py:
from . import app
from .models import User
from .utils.auth import login_required
@app.route('/')
def index():
return 'Hello, world!'
This structure separates concerns: templates and CSS (remember CSS3 and layouts) live under static/ and templates/, Python code lives under the package, and run.py is a tiny launcher (or you use a WSGI server in production).
Relative imports vs absolute imports
- Absolute import:
from myapp.utils.auth import login_required— explicit, clear, and preferred for larger apps. - Relative import:
from .utils.auth import login_required— useful within a package to avoid long names.
Relative imports only work inside packages (i.e., when your module is part of a package), not when you run a module as a script directly. To run modules for development, use the package context: python -m myapp.run.
Common pitfalls and best practices
Circular imports
- Symptom: ImportError or partially initialized module.
- Fix: Move shared code to a third module (e.g.,
utils) or use local imports inside functions.
Global state in modules
- Modules are singletons; module-level variables persist across imports.
- Prefer factories (functions that create objects) or explicit state containers.
Keep modules focused
- Follow single-responsibility:
models.pyhandles DB models;routes.pyhandles routes;utils/has pure helpers.
- Follow single-responsibility:
Use virtual environments and requirements.txt
- For web projects, isolate dependencies:
python -m venv venvthenpip install -r requirements.txt.
- For web projects, isolate dependencies:
Hot reload and importlib
- During development, frameworks reload code. If you need to programmatically reload a module,
importlib.reload(module)exists, but rely on framework tooling when possible.
- During development, frameworks reload code. If you need to programmatically reload a module,
Packaging and dependencies
- Use
requirements.txtorpyproject.tomlto pin dependencies. This is how you share the environment with teammates and deploy reliably. - When a module becomes reusable, consider making it an installable package with
setup.cfg/pyproject.toml.
Quick comparison table
| Concept | When to use | Example |
|---|---|---|
| Module | One file of related code | validators.py with functions is_email() |
| Package | Multiple modules under a namespace | myapp.utils with auth.py, validators.py |
| Absolute import | Clear in large apps | from myapp.utils.auth import login_required |
| Relative import | Shorter paths inside packages | from .auth import login_required |
Small example: utils/auth.py
# myapp/utils/auth.py
def login_required(handler):
def wrapper(*args, **kwargs):
# pseudo-check: in real apps, integrate with Flask's session or decorators
print('Checking login...')
return handler(*args, **kwargs)
return wrapper
Then in routes.py:
from .utils.auth import login_required
@login_required
def protected():
return 'Secret content'
This shows how you can split auth logic away from route definitions — much cleaner than copy-pasting checks everywhere.
Key takeaways
- Modules = files, Packages = folders with init.py.
- Use packages to keep web app concerns separated: routes, models, templates, static CSS/JS.
- Prefer absolute imports for clarity; use relative imports inside packages when it shortens paths sensibly.
- Watch out for circular imports and global state; prefer small, focused modules.
- Use virtual environments and
requirements.txtto manage third-party packages.
"A well-structured package is like good UI: you stop noticing it because everything just works and you can get things done." — your future less-stressed self
If you want, I can: provide a ready-to-clone starter project structure, show how to package a module for pip, or walk through resolving a circular import step-by-step. Which one will save your future debugging-self the most time?
Comments (0)
Please sign in to leave a comment.
No comments yet. Be the first to comment!