Modules, packages & imports
Splitting code into modules, import styles, __name__ == "__main__", sys.path, packages and relative imports.
As programs grow, one giant file becomes impossible to navigate. Python's answer is modules (single files) and packages (folders of modules). Importing lets you reuse your own code across files and tap into the enormous standard library and the hundreds of thousands of third-party packages on PyPI.
Every .py file is a module#
Create two files in the same folder:
import shapes runs shapes.py once, creates a module object, and binds it to the name shapes. You access its contents with a dot: shapes.circle_area. Keeping the module prefix makes it obvious where every name comes from.
Import styles#
Guidelines (from PEP 8):
- Put imports at the top of the file, one per line, grouped: standard library, then third-party packages, then your own modules — with a blank line between groups.
- Prefer
import moduleorfrom module import name. Avoidfrom module import *: it pulls in unknown names and makes it hard to tell where anything came from. - Use well-known aliases (
import numpy as np,import pandas as pd) but don't invent cryptic ones.
Modules run once#
Python caches imported modules in sys.modules. Importing the same module again, from anywhere in your program, reuses the existing object rather than re-running the file:
This is also why module-level code should mostly be definitions — functions, classes, constants — not work that runs on import.
if __name__ == "__main__":#
When a file is run directly, Python sets its __name__ to "__main__"; when it's imported, __name__ is the module's name. Use that to make a file both importable and runnable:
Running python3 temperature.py directly prints the conversion table.
How Python finds modules: sys.path#
When you import something, Python searches a list of directories stored in sys.path, in order:
- The directory of the script you ran (or the current directory in the REPL).
- Directories in the
PYTHONPATHenvironment variable, if set. - The standard library.
site-packages— wherepipinstalls third-party packages (inside your virtual environment, if one is active).
Because your script's folder comes first, naming a file after a standard module — random.py, json.py, email.py, test.py — silently shadows the real one. If an import behaves strangely, check for a file with a clashing name (and delete any stale __pycache__).
If a module can't be found you get:
Packages: folders of modules#
A package is a directory containing modules, normally with an __init__.py file (which may be empty). Packages can nest to any depth:
Key points:
- Dots in an import path follow folders:
shop.models.productisshop/models/product.py. __init__.pyruns when the package is first imported. It's the place to expose a clean public API, asshop/__init__.pydoes above.- Absolute imports (
from shop.pricing import ...) work from anywhere and are preferred in application code. Relative imports (from .pricing,from ..pricing) work only inside a package, and are useful within libraries. __all__lists the names thatfrom shop import *would export — and documents the public API.
Running a module inside a package with -m#
If a module inside a package uses relative imports, running it by file path (python3 shop/models/product.py) fails with "attempted relative import with no known parent package". Run it as a module from the project root instead:
The -m flag also runs standard-library and installed tools: python3 -m venv, python3 -m pip, python3 -m http.server, python3 -m pytest.
Exploring modules#
The REPL is the best place to poke around a module you don't know yet:
help(math) shows the full documentation, and math.__file__ shows where a (pure-Python) module lives on disk.
Circular imports#
If a.py imports b.py and b.py imports a.py, one of them will see a half-initialised module and you'll get an ImportError or AttributeError. Fixes: move shared code into a third module, import inside the function that needs it, or rethink the design — circular imports usually signal two modules that are too tightly coupled.
Common mistakes#
- Naming files after standard modules (
random.py,json.py,test.py). - Running package modules by file path — use
python3 -m package.modulefrom the project root. - Putting heavy work at module level — it runs on every import. Wrap it in
main(). - Forgetting that imports are cached — edits to an imported module aren't picked up in a running REPL until you restart it (or use
importlib.reload).
What's next#
You can organise your own code — now let's use other people's. Next: pip and virtual environments, the essential tools for installing third-party packages safely.
Check your understanding
Quick quiz
1.What is a Python module?
2.Why is
from module import *discouraged?3.You named your script
random.py, andimport randomin it no longer gives yourandom.randint. Why?
Finished reading?
Mark this lesson complete to track your progress.