Python Modules and Packages: Organizing Code Across Files
- Any .py file is a module; importing it runs the file once and exposes its top-level names.
- A package is a directory containing modules, traditionally marked with an
__init__.pyfile. - if __name__ == "__main__": lets a file be both importable and runnable directly.
- Python searches sys.path, in order, to resolve an import — the current directory and installed packages included.
What counts as a module
Any file ending in .py is a module. Its top-level names — functions, classes, variables — become available to whatever imports it. Given a file shapes.py:
# shapes.py
import math
def circle_area(radius):
return math.pi * radius ** 2
another file in the same directory can use it:
# main.py
import shapes
print(shapes.circle_area(3))
Importing a module runs its code from top to bottom exactly once per process, no matter how many other files import it afterwards; subsequent imports reuse the already-loaded module object from sys.modules.
Different import forms
Python offers several import styles, each with different name-binding behaviour:
import shapes # access as shapes.circle_area
from shapes import circle_area # access as circle_area directly
from shapes import circle_area as area # renamed on import
import shapes as sh # module itself renamed
from module import * pulls every public name into the current namespace and is generally discouraged outside of quick interactive use — it makes it unclear where a given name came from, and can silently overwrite existing names.
Packages and __init__.py
A package is a directory of modules that Python treats as a single importable unit. Traditionally it contains an __init__.py file, which runs when the package is first imported and can be empty or can expose selected names:
myproject/
geometry/
__init__.py
shapes.py
volumes.py
main.py
# main.py
from geometry import shapes
from geometry.volumes import cube_volume
Since Python 3.3, a directory without an __init__.py can still work as a namespace package, but an explicit __init__.py remains the clearer, more predictable choice for ordinary projects, and is still useful for controlling what a bare from geometry import * exposes.
The __name__ == "__main__" guard
Every module has a built-in __name__ variable. When a file is run directly, __name__ is set to "__main__"; when the same file is imported by another module, __name__ is set to the module's own name instead:
# shapes.py
def circle_area(radius):
return 3.14159 * radius ** 2
if __name__ == "__main__":
print(circle_area(2)) # only runs when shapes.py is executed directly
This guard is what lets a file serve two purposes: a library that other code imports quietly, and a script with its own test or demo output when run on its own.
How Python finds modules
When you write import shapes, Python searches, in order: the directory of the script being run (or the current directory in an interactive session), the directories listed in the PYTHONPATH environment variable, and the standard installation directories — including any packages installed with pip. The full search list is available at runtime as sys.path, and can be inspected directly:
import sys
print(sys.path)
If an import fails with ModuleNotFoundError, the fix is almost always either installing the missing package into the active environment or fixing the directory structure so the importing file's search path actually includes the module you meant to reach.
Relative imports inside a package
Inside a package, one module can import a sibling module using a relative import — a leading dot means "the current package," two dots means "the parent package," and so on:
# geometry/volumes.py
from .shapes import circle_area # sibling module in the same package
def cylinder_volume(radius, height):
return circle_area(radius) * height
Relative imports only work inside a package that Python recognises as such — running the file directly as a standalone script bypasses that package context and raises ImportError: attempted relative import with no known parent package. Because of this, files meant to be imported as part of a package should generally be run through the package's entry point (or with python -m package.module), not executed directly as a loose script.
Controlling exports with __all__
A module or package's __init__.py can define a list named __all__ to control exactly what from module import * pulls in, overriding the default of "every name that does not start with an underscore":
# geometry/__init__.py
from .shapes import circle_area
from .volumes import cube_volume
__all__ = ["circle_area", "cube_volume"]
With __all__ defined, from geometry import * imports only circle_area and cube_volume, even if other names happen to be present in the package's namespace. __all__ has no effect on explicit imports like from geometry import circle_area, which always work regardless of the list — it only governs the star-import form, and mainly serves as documentation of a package's intended public surface.