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 importsfrom 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.