Understanding Python Modules and Packages
SkillVeris Team
Engineering Team

A Python module is any single .py file, and a package is a directory of modules that Python can import as one namespace.
In this guide, you'll learn:
- The import statement runs a module once, caches it in sys.modules, and binds its names into your current namespace.
- Packages group related modules together and are usually marked by an __init__.py file that controls what the package exposes.
- Absolute imports (from myapp.utils import helper) are clearer and safer than relative imports for most projects.
- The if __name__ == '__main__' guard lets a file work both as an importable module and as a runnable script.
1What Is a Module and a Package?
A Python module is simply a file ending in .py that contains functions, classes, and variables you can reuse elsewhere. A package is a folder containing one or more modules, letting you group related code under a single importable name. Together they are how Python keeps large programs organized instead of forcing everything into one enormous file.
When you write import math or from datetime import date, you are using modules from the standard library. Your own files work exactly the same way. Understanding this system is the difference between a script that grows unmanageable and a project that scales cleanly.
2How the import Statement Works
When Python imports a module, it locates the file, executes its code top to bottom exactly once, and stores the result in a cache called sys.modules. Later imports of the same module reuse that cached object instead of re-running the file. This is why import side effects only fire the first time.
- import math # bind the whole module; access as math.sqrt
- from math import sqrt # bind just the name sqrt
- from math import sqrt as square_root # rename on import
- import numpy as np # common aliasing convention
- from mypackage.utils import clean # reach into a package
💡Pro Tip
Avoid from module import * in real code. It hides where names came from and can silently overwrite existing names in your namespace.
3Creating Your Own Module
Any .py file you write is already a module. Save a file named calculations.py with a function inside, and any other file in the same folder can run from calculations import add. Python finds it because the current directory is on the module search path, sys.path.
The __name__ Guard
When a file runs directly, its __name__ variable equals the string '__main__'. When it is imported, __name__ equals the module's name instead. The common guard below lets the same file serve as both an importable library and a standalone script.
def add(a, b):
return a + b
if __name__ == '__main__':
print(add(2, 3)) # only runs when executed directly4Building a Package
A package is a directory Python treats as an importable unit. Traditionally you place an __init__.py file inside it, which can be empty or can expose selected names. Since Python 3.3, namespace packages can work without __init__.py, but including it remains the clearest and most predictable choice.
- myapp/ # the package directory
- __init__.py # marks it as a package
- models.py # a module inside the package
- utils.py # another module
- data/ # a subpackage
- __init__.py
- loader.py
What __init__.py Does
The __init__.py file runs when the package is first imported. Use it to promote key names so callers write from myapp import User instead of from myapp.models import User. Setting __all__ controls what a wildcard import exposes.
5Absolute vs Relative Imports
Absolute imports spell out the full path from the project root, such as from myapp.data.loader import load. Relative imports use dots to mean the current or parent package, such as from .loader import load or from ..utils import clean. Absolute imports are easier to read and survive files being moved, so prefer them unless you have a strong reason not to.
⚠️Watch Out
Relative imports only work inside a package that is being imported, not in a file you run directly with python file.py. Running such a file often raises 'attempted relative import with no known parent package'.
6The Module Search Path
Python decides where to look for modules using sys.path, a list of directories checked in order. It usually includes the script's directory, the standard library location, and installed third-party packages. If an import fails with ModuleNotFoundError, the module simply is not on any path Python searched.
- The directory of the script being run comes first.
- Paths listed in the PYTHONPATH environment variable come next.
- Standard library and site-packages directories follow.
- Print import sys; print(sys.path) to see the exact list.
7Common Mistakes to Avoid
Most import errors come from a handful of avoidable habits rather than anything mysterious in Python's design.
- Naming your file the same as a standard library module, like random.py, which shadows the real one and breaks imports.
- Creating circular imports where module A imports B and B imports A; refactor shared code into a third module.
- Relying on wildcard imports that make it impossible to tell where a name originated.
- Forgetting __init__.py in older setups and wondering why the package will not import.
- Running a file directly and expecting its relative imports to resolve.
8Key Takeaways
The module and package system is the backbone of every serious Python project.
- A module is one .py file; a package is a directory of modules.
- import runs a module once and caches it in sys.modules.
- __init__.py marks a package and can curate what it exposes.
- Prefer absolute imports for clarity and refactor safety.
- Use the __name__ == '__main__' guard so files work as both library and script.
9Frequently Asked Questions
Q: What is the difference between a module and a package in Python? A: A module is a single .py file containing reusable code, while a package is a directory that groups multiple modules under one importable namespace, usually marked by an __init__.py file.
Q: Do I still need __init__.py in modern Python? A: Not always. Since Python 3.3 namespace packages can work without it, but including an __init__.py is clearer, avoids surprises, and gives you a place to curate the package's public API.
Q: Why do I get ModuleNotFoundError even though the file exists? A: The file's directory is probably not on sys.path. Check that you are running from the right location, or adjust PYTHONPATH so Python can find it.
Q: Should I use absolute or relative imports? A: Prefer absolute imports for most code because they are explicit and survive file moves. Reserve relative imports for tightly coupled modules inside the same package.
Get The Print Version
Download a PDF of this article for offline reading.
About the Publisher
SkillVeris Team
Engineering Team
Our engineering writers turn abstract code concepts into hands-on, project-driven learning experiences.
View all postsRelated Posts
Never miss an update
Get the latest tutorials and guides delivered to your inbox.
No spam. Unsubscribe anytime.