Modules & Packages
Topic 5 of 7, with 3 concept checks. Organizing code across files with import, __name__, and __init__.py
Turn Python files into stable import boundaries
Import boundaries
Understand how modules are located and initialized, what a package exports, and why import direction and executable module guards matter to reusable interview code.
Core lesson 01
__name__ is '__main__' only when a file is run directly (python file.py), and the module's own name when imported elsewhere — the guard lets a file provide both reusable functions and a runnable script.
Python sets a special variable __name__ in every module. When you run a file directly, Python sets that file's __name__ to '__main__'. When the same file is imported from elsewhere, __name__ is set to the module's actual name instead. Wrapping script-only code (a demo, a CLI entry point) in if __name__ == "__main__": means that code only runs when the file is executed directly, not when another file imports functions from it.
# mathutils.py
def add(a, b):
return a + b
if __name__ == "__main__":
print("running self-test:", add(2, 3))
# running `python mathutils.py` prints the self-test
# `import mathutils` elsewhere does NOT run that printWhat to remember
What does if __name__ == "__main__": do and why is it used?
Common footguns
- Putting real logic (not just a demo/test) inside the __main__ guard, making it impossible for other modules to reuse that logic by importing it.
Core lesson 02
A module is a single .py file. A package is a directory of modules, historically marked by __init__.py, which runs on first import and can control what the package exposes.
Any .py file is a module — import mathutils imports mathutils.py. A package groups related modules under a directory, e.g. mypackage/geometry.py, importable as mypackage.geometry. __init__.py (even if empty) runs when the package is first imported; it's the conventional place to control what from mypackage import * exposes, or to re-export submodules for a cleaner public API.
# project structure:
# mypackage/
# __init__.py
# geometry.py
# algebra.py
# mypackage/__init__.py
from .geometry import area_of_circle
# elsewhere:
from mypackage import area_of_circle # works because __init__.py re-exported itWhat to remember
What's the difference between a module and a package, and what does __init__.py do?
Common footguns
- Circular imports between modules in the same package — module A imports from B while B imports from A, causing partial/undefined-name errors depending on import order.
Core lesson 03
Absolute imports (import mypackage.geometry) name the full path from the top-level package. Relative imports (from .geometry import area) are relative to the current module's position, using dots for how many levels up.
Absolute imports are unambiguous and generally preferred for clarity — they reference the top-level package name regardless of where the importing file lives. Relative imports (. for the current package, .. for the parent) are convenient inside a package for referring to sibling modules without hardcoding the package's name, but only work inside a package context, not in a standalone script run directly.
# inside mypackage/algebra.py
from . import geometry # relative: sibling module
from mypackage import geometry # absolute: same result, explicit path
from ..otherpackage import tool # relative: one level up, into a sibling packageWhat to remember
What's the difference between absolute and relative imports?
Common footguns
- Using relative imports in a script meant to be run directly with python file.py — relative imports require the file to be part of a package context, raising 'attempted relative import with no known parent package'.
Python lab
Browser Python lab
Runtime · idle
Python loads on your first run. Your code stays in this browser.
Best practices
- Prefer absolute imports for clarity, especially in larger codebases.
- Keep __init__.py minimal — mostly re-exports, not heavy logic.
- Avoid circular imports by moving shared code into a separate module both sides can import from.
- Use if __name__ == "__main__": to separate reusable logic from script-only behavior.
Apply the concept in Interview practice
Design In-Memory File SystemhardLeetCode #588 · O(path length) per operation
Model directories and files as a tree of dict nodes; split paths on '/' and walk/create nodes level by level for ls/mkdir/addContentToFile/readContentFromFile.
Open problemDesign File SystemmediumLeetCode #1166 · O(path length) per operation
Store paths directly as dict keys mapping to values; createPath checks the parent path exists and the new path doesn't, mirroring how package/module paths nest.
Open problemConcept checks
What does if __name__ == "__main__": do and why is it used?
Hint
Every module has a hidden variable set differently depending on how it's run.
It's 'main' only when the file is executed directly, not when imported.
Answer
__name__ is '__main__' only when a file is run directly (python file.py), and the module's own name when imported elsewhere — the guard lets a file provide both reusable functions and a runnable script.
Python sets a special variable __name__ in every module. When you run a file directly, Python sets that file's __name__ to '__main__'. When the same file is imported from elsewhere, __name__ is set to the module's actual name instead. Wrapping script-only code (a demo, a CLI entry point) in if __name__ == "__main__": means that code only runs when the file is executed directly, not when another file imports functions from it.
# mathutils.py
def add(a, b):
return a + b
if __name__ == "__main__":
print("running self-test:", add(2, 3))
# running `python mathutils.py` prints the self-test
# `import mathutils` elsewhere does NOT run that printWatch out
- Putting real logic (not just a demo/test) inside the __main__ guard, making it impossible for other modules to reuse that logic by importing it.
What's the difference between a module and a package, and what does __init__.py do?
Hint
One is a single file, the other is a directory of them.
__init__.py is what (historically) marked a directory as importable.
Answer
A module is a single .py file. A package is a directory of modules, historically marked by __init__.py, which runs on first import and can control what the package exposes.
Any .py file is a module — import mathutils imports mathutils.py. A package groups related modules under a directory, e.g. mypackage/geometry.py, importable as mypackage.geometry. __init__.py (even if empty) runs when the package is first imported; it's the conventional place to control what from mypackage import * exposes, or to re-export submodules for a cleaner public API.
# project structure:
# mypackage/
# __init__.py
# geometry.py
# algebra.py
# mypackage/__init__.py
from .geometry import area_of_circle
# elsewhere:
from mypackage import area_of_circle # works because __init__.py re-exported itWatch out
- Circular imports between modules in the same package — module A imports from B while B imports from A, causing partial/undefined-name errors depending on import order.
What's the difference between absolute and relative imports?
Hint
One spells out the full path from the project root; the other is relative to the current module's location.
Relative imports use leading dots.
Answer
Absolute imports (import mypackage.geometry) name the full path from the top-level package. Relative imports (from .geometry import area) are relative to the current module's position, using dots for how many levels up.
Absolute imports are unambiguous and generally preferred for clarity — they reference the top-level package name regardless of where the importing file lives. Relative imports (. for the current package, .. for the parent) are convenient inside a package for referring to sibling modules without hardcoding the package's name, but only work inside a package context, not in a standalone script run directly.
# inside mypackage/algebra.py
from . import geometry # relative: sibling module
from mypackage import geometry # absolute: same result, explicit path
from ..otherpackage import tool # relative: one level up, into a sibling packageWatch out
- Using relative imports in a script meant to be run directly with python file.py — relative imports require the file to be part of a package context, raising 'attempted relative import with no known parent package'.