Packages: Organize Modules for Scalable Projects
Introduction
In this chapter, you will learn Python packages and how they help you organize multiple modules in a clean project structure. A package is essential when your project grows beyond a few files. Once you understand packages, your codebase becomes easier to maintain, reuse, and scale.
Prerequisites
- Python
3.10+installed - Basic understanding of modules and import statements
- Ability to run
.pyfiles in terminal or IDE
What Is a Package
A package is a directory that groups related Python modules.
Typical package structure:
my_project/
├── app.py
└── utils/
├── __init__.py
├── math_tools.py
└── text_tools.pyIn many projects, __init__.py is used to mark and manage package behavior.
Why packages matter:
- better code organization
- clearer import paths
- easier teamwork and testing
1) Create and Import from a Package
Suppose you have:
utils/math_tools.pyapp.py
utils/math_tools.py:
# Add two numbers
def add(a, b):
return a + bapp.py:
# Import function from package module
from utils.math_tools import add
print(add(3, 5))This is the simplest package import workflow.
2) Use __init__.py
__init__.py can:
- mark directory as a package (traditional usage)
- expose selected members
- run package initialization logic
Example utils/__init__.py:
# Re-export functions for cleaner import paths
from .math_tools import add
from .text_tools import to_upperThen you can import directly from package:
from utils import add, to_upperTip
Best Practice
Use __init__.py to expose a clean public API and hide internal details.
3) Absolute vs Relative Import
Absolute Import
from utils.math_tools import addRelative Import (inside package modules)
from .math_tools import addRules of thumb:
- use absolute imports in app entry files
- use relative imports inside the same package when appropriate
4) Subpackages
Large projects often use nested package structures.
shop/
├── __init__.py
├── services/
│ ├── __init__.py
│ └── order_service.py
└── models/
├── __init__.py
└── order.pyThis keeps domains separated and improves maintainability.
5) Real Mini Example: Score Package
Build a package for score utilities.
Structure:
score_app/
├── main.py
└── score_utils/
├── __init__.py
├── calc.py
└── level.pyscore_utils/calc.py:
# Calculate average score
def average(scores):
return sum(scores) / len(scores)score_utils/level.py:
# Convert score to grade level
def grade(score):
if score >= 90:
return "A"
if score >= 80:
return "B"
if score >= 70:
return "C"
return "D"score_utils/__init__.py:
from .calc import average
from .level import grademain.py:
from score_utils import average, grade
scores = [95, 82, 76]
avg = average(scores)
print(f"Average: {avg:.2f}")
print(f"Grade of first score: {grade(scores[0])}")This shows how packages make multi-module projects clean and reusable.
6) Common Package Pitfalls
Module Not Found
Often caused by:
- wrong working directory
- broken import path
- naming conflicts
Name Conflicts
Avoid file/package names that collide with standard library modules (for example json, random, os).
Circular Imports
If modules import each other directly, import errors may occur.
Warning
Do not over-split tiny scripts into too many packages.
Package structure should match project complexity.
Common Beginner Mistakes
Mistake 1: Missing or Misusing __init__.py
Package behavior can become unclear without a clear package entry design.
Mistake 2: Confusing Relative and Absolute Imports
This often causes import errors when running files from different locations.
Mistake 3: Running Internal Package File Directly
Executing deep internal files directly can break expected import context.
Surprise Practice Challenge
Build a "mini_blog" package:
mini_blog/posts.pyfor post data functionsmini_blog/users.pyfor user data functionsmini_blog/__init__.pyto expose key APIsmain.pyimports package APIs and prints a simple report- Keep import paths clean and consistent
If you finish this, you are building code structure like real Python projects.
FAQ
What is the difference between module and package?
A module is one .py file; a package is a folder containing multiple modules.
Is __init__.py always required?
Modern Python supports namespace packages, but using __init__.py is still common and recommended for clarity.
Should I always use subpackages?
Only when project size or domain complexity justifies it.
Why does code run in IDE but fail in terminal import?
Usually because working directory and execution context differ, affecting module search paths.