Any .py file is automatically a module that other files can import — no special declaration needed — and a folder of related modules can be organized into a package.
# math_utils.py
def add(a, b):
return a + b
PI = 3.14159
# main.py
import math_utils
print(math_utils.add(2, 3)) # 5
print(math_utils.PI)
# or:
from math_utils import add, PI
print(add(2, 3))
myproject/
├── main.py
└── utils/
├── __init__.py # marks 'utils' as a package (can be empty)
├── math_utils.py
└── string_utils.py
from utils.math_utils import add # importing from inside a package
from utils import math_utils, string_utils
A package is simply a directory containing an __init__.py file (which can be empty, or used to control what the package exposes) plus one or more module files — this lets related functionality be organized hierarchically instead of dumping every function into a single giant file. Since Python 3.3, __init__.py is technically optional for basic imports to work (a "namespace package"), but including it is still the conventional, safer choice for a real package, since it gives you an explicit place to control imports and run package-level setup code.
Splitting code into modules and packages as a project grows keeps related functionality together, avoids one file becoming unmanageably large, and lets different parts of a codebase be tested, imported, and reasoned about independently.