As a Python project grows, keeping every function and class in one file becomes difficult. Python modules and packages solve that problem by dividing code into focused, reusable parts.
A module is usually one .py file. A package is a directory that groups related modules. Together, they help you create clearer projects, reuse code, write tests, and collaborate without turning one script into thousands of lines.
What is a Python module?
Any Python file can act as a module. Suppose a project contains a file named calculator.py:
# calculator.py
def add(a, b):
return a + b
def subtract(a, b):
return a - b
PI = 3.14159
Another file can import and use it:
# main.py
import calculator
print(calculator.add(10, 5))
print(calculator.PI)
The module name is the filename without .py. The official Python modules tutorial explains how Python loads files and creates module namespaces.
Different import styles
The regular import form keeps the module name visible:
import calculator
result = calculator.add(4, 8)
You can import specific names:
from calculator import add, subtract
print(add(4, 8))
Aliases are useful for long or conventional names:
import calculator as calc
print(calc.subtract(20, 3))
Avoid from module import *. It hides where names come from and can silently overwrite variables. Clear imports support the naming and organization rules discussed in the PEP 8 guide.
How Python finds modules
When Python sees an import, it checks several locations, including the current project, the standard library, installed packages, and directories listed in sys.path.
import sys
for folder in sys.path:
print(folder)
A common mistake is naming your own file after a standard or third-party module. A file called random.py, json.py, or requests.py can shadow the real library and cause confusing import errors. The guide to fixing ModuleNotFoundError covers environment and path problems in detail.
What is a Python package?
A package is a directory containing modules that belong together. A small store application might use this structure:
store_app/
├── main.py
└── store/
├── __init__.py
├── products.py
├── orders.py
└── reports.py
The store directory is the package. Each Python file inside it is a submodule.
# store/products.py
def calculate_total(price, quantity):
return price * quantity
# main.py
from store.products import calculate_total
print(calculate_total(19.90, 3))
What does __init__.py do?
The __init__.py file marks a regular package and can define the public interface that users import. It may be empty, but it can also re-export selected names:
# store/__init__.py
from .products import calculate_total
__all__ = ["calculate_total"]
Now the application can use:
from store import calculate_total
Keep __init__.py lightweight. Running database connections, network requests, or expensive calculations during import makes applications slow and unpredictable.
Absolute and relative imports
An absolute import begins at the top-level package:
from store.products import calculate_total
A relative import uses dots to refer to nearby modules:
# store/reports.py
from .products import calculate_total
Absolute imports are often easier to understand in application code. Relative imports can be convenient inside a cohesive package. Consistency is more important than mixing styles without a reason.
Using __name__ and running a module
Every module has a __name__ variable. When a file runs directly, its value is "__main__". When imported, it contains the module name.
# temperature.py
def celsius_to_fahrenheit(value):
return value * 9 / 5 + 32
if __name__ == "__main__":
print(celsius_to_fahrenheit(25))
This guard prevents demonstration or command-line code from running merely because the module was imported.
Packages can also be executed with python -m package.module. This approach preserves the import context and often avoids path errors.
Designing a useful project structure
A beginner-friendly application might look like this:
expense_tracker/
├── README.md
├── pyproject.toml
├── src/
│ └── expense_tracker/
│ ├── __init__.py
│ ├── models.py
│ ├── storage.py
│ └── cli.py
└── tests/
├── test_models.py
└── test_storage.py
The src layout helps prevent accidental imports from the project folder. Tests live separately but import the installed package. The article about unit testing in Python shows how this separation improves test organization.
Modules, packages, libraries, and distributions
These terms are related but not identical:
- Module: one importable Python file.
- Package: an importable directory of modules.
- Library: a general term for reusable code.
- Distribution: the installable project published to a package index.
A distribution can contain one or more packages. The guide to Python libraries explains how reusable code is installed and used, while the tutorial on creating an installable package covers packaging metadata and builds.
A practical example
Consider a package that formats customer data:
customer_tools/
├── __init__.py
├── names.py
└── validation.py
# customer_tools/names.py
def normalize_name(name):
return " ".join(part.capitalize() for part in name.split())
# customer_tools/validation.py
def is_valid_email(email):
return "@" in email and "." in email.rsplit("@", 1)[-1]
# customer_tools/__init__.py
from .names import normalize_name
from .validation import is_valid_email
from customer_tools import normalize_name, is_valid_email
name = normalize_name(" aLEX johnSON ")
email = "[email protected]"
print(name)
print(is_valid_email(email))
This organization gives each module one responsibility and creates a simple public interface.
Avoiding circular imports
A circular import happens when module A imports module B while module B imports module A. Python may encounter a partially initialized module and raise an error.
Common solutions include moving shared definitions into a third module, importing a dependency inside a function only when necessary, or redesigning responsibilities so modules do not depend on each other in both directions.
package/
├── common.py
├── users.py
└── orders.py
Both users.py and orders.py can import shared types from common.py without importing each other.
Common mistakes
Running an internal file directly
Executing python package/module.py can break relative imports. Run it from the project root with python -m package.module.
Depending on the current working directory
Build file paths from __file__ or use pathlib rather than assuming the terminal always starts in one folder.
Putting unrelated code in one package
A package should represent a coherent feature or domain. Split independent concerns into separate packages.
Creating too many tiny modules
Organization should reduce complexity, not create a maze. A module with one trivial function is not automatically better than a clear, focused file containing several related functions.
Best practices
- Use lowercase module and package names.
- Give each module a clear responsibility.
- Keep imports at the top unless a local import solves a specific problem.
- Expose a small, intentional public API.
- Avoid side effects during import.
- Use virtual environments to isolate dependencies. The venv tutorial explains the workflow.
- Add tests before reorganizing a mature project.
Conclusion
Python modules and packages turn a growing script into a maintainable application. Begin by moving related functions into modules, group those modules into packages, use explicit imports, and keep dependencies flowing in one clear direction.
For deeper reference, review the official Python import system documentation. A small project organized into two or three modules is the best place to practice before designing a larger installable package.





