Packages & __main__ Interview Questions & Answers
5 questions Updated 2026-06-18
Python interview questions on packages vs modules, the role of __init__.py, python -m and __main__.py, __all__, namespace packages, and running a script versus importing it.
A module is a single .py file. A package is a directory of modules
that you import as a unit, traditionally marked by an __init__.py file. That
file runs when the package is first imported, so it's where you can set the
package's public API or do setup; it's often empty, which is fine.
# myapp/ <- package
# __init__.py <- marks it a package, runs on import
# db.py <- module
# utils/ <- subpackage
# __init__.py
# text.py
import myapp.db # import a module from the package
from myapp.utils.text import slug
A common use of __init__.py is to re-export so callers can write
from myapp import db cleanly. Modules organize code into files; packages organize
modules into a namespace tree.
python -m pkg.module runs a module as a script while still locating it via
the import system (so relative imports and the package context work). When you point
-m at a package, Python runs that package's __main__.py — that's how a
package becomes executable, like python -m http.server.
python -m http.server 8000 # runs http.server's __main__.py
python -m myapp # runs myapp/__main__.py
python -m pytest # run an installed tool as a module
# myapp/__main__.py
from myapp.cli import main
main()
Use -m to run installed/packaged code by its import name rather than a file path,
which avoids the sys.path surprises you get from running a file directly.
__all__ is a list of names that defines a module's (or package's) public API for
wildcard imports — from module import * imports exactly those names. Without it,
import * grabs every name not starting with an underscore.
# mymath.py
__all__ = ["add", "PI"] # only these are exported by *
def add(a, b): return a + b
def _helper(): ... # private anyway
PI = 3.14159
# elsewhere
from mymath import * # gets add and PI only
It does not prevent explicit imports (from mymath import _helper still works)
— it only curates * and documents intent. Define __all__ to keep import *
clean and to signal what's officially public.
A namespace package (PEP 420) is a package with no __init__.py whose
contents can be split across multiple directories on sys.path. Python merges
those directories into one logical package — useful for plugins where different
distributions contribute to a shared top-level namespace.
# path1/acme/foo.py
# path2/acme/bar.py (no __init__.py in either acme/)
# with both paths on sys.path:
import acme.foo
import acme.bar # both resolve under the merged 'acme' namespace
Since Python 3.3, a directory without __init__.py can still be importable as a
namespace package. For an ordinary single-location package you usually still want a
regular package (with __init__.py); reserve namespace packages for splitting a
namespace across separately-installed parts.
When you run a file (python foo.py), Python sets its __name__ to
"__main__". When you import it, __name__ is the module's name. The
if __name__ == "__main__": guard uses this so a file can act as both a runnable
script and an importable library — the guarded code runs only on direct execution.
# greet.py
def hello(name): return f"Hi {name}"
if __name__ == "__main__": # runs only via `python greet.py`
print(hello("world")) # NOT run when `import greet`
Without the guard, your script's top-level side effects (running, printing, parsing
args) would fire every time the module is imported. Always put script entry
points behind the __main__ guard.
Practice tests are coming soon
Get notified when interactive mock interviews and quizzes launch.