What is the difference between modules and packages? What does __init__.py do?
A **module** in Python is any single `.py` file. When you write `import utils`, Python finds `utils.py` on the module search path (`sys.path`) and executes it, caching the result in `sys.modules`. The module's top-level names become attributes on the imported object.
A **package** is a directory that contains one or more modules. What makes it a package rather than a plain directory is the presence of an `__init__.py` file. When Python encounters `import mypackage`, it executes `mypackage/__init__.py` and treats its namespace as the package's namespace.
**What `__init__.py` does:**
1. **Marks the directory as a package** — without it, Python 2 would not recognize the directory as importable. In Python 3.3+, namespace packages allow directories without `__init__.py`, but regular packages with it are still the standard. 2. **Controls the public API** — you import and expose names here so consumers can write `from mypackage import MyClass` instead of `from mypackage.submodule import MyClass`. This decouples internal structure from external interface. 3. **Runs initialization code** — database connections, plugin registration, or configuration loading at import time. Keep this minimal to avoid slow imports. 4. **Defines `__all__`** — a list of names that `from mypackage import *` will export. Without `__all__`, star imports export everything not prefixed with an underscore.
**Relative imports** use dot notation to refer to siblings within the same package: `from . import sibling` or `from ..parent import thing`. They only work inside a package, not in top-level scripts.
A **namespace package** (PEP 420) is a package spread across multiple directories with no `__init__.py`, useful for plugin systems or splitting a large package across repositories. Python merges the directories into a single package namespace.
The practical impact: well-structured `__init__.py` files let you refactor internal module organization without breaking external imports, because consumers import from the package, not the internal file path.
Correctly distinguishes module vs package, explains __init__.py's role in marking a directory and controlling API.
Covers __all__, relative imports, namespace packages, and explains the decoupling benefit of __init__.py as an API surface.
Reading the answer is step one. Explaining it unprompted — under interview pressure — is what actually matters. Get AI-graded feedback on your answer with follow-up probes on your weak points.
Get Graded — Free Assessment