Python Basics — Packages & init.py
Packages & __init__.py
When your project grows beyond a few modules, you organize them into packages — directories containing related modules.
Package Directory Structure
A package is simply a directory with Python files:
myproject/
├── main.py
└── mypackage/
├── __init__.py
├── math_utils.py
├── string_utils.py
└── validators.py
__init__.py Purpose
The __init__.py file serves two purposes:
- Marks the directory as a Python package (required before Python 3.3)
- Runs when the package is imported — use it to set up the package
Minimal __init__.py
# mypackage/__init__.py
# Can be empty — just marks this as a package
__init__.py with Exports
# mypackage/__init__.py
"""My utility package."""
from .math_utils import add, multiply
from .string_utils import capitalize_words
from .validators import is_valid_email
__all__ = ["add", "multiply", "capitalize_words", "is_valid_email"]
__version__ = "1.0.0"
Now users can import directly from the package:
# Instead of:
from mypackage.math_utils import add
# Users can do:
from mypackage import add
Using Package Modules
# mypackage/math_utils.py
def add(a, b):
return a + b
def multiply(a, b):
return a * b
# mypackage/string_utils.py
def capitalize_words(text):
return " ".join(word.capitalize() for word in text.split())
# main.py
# Full module path
from mypackage.math_utils import add
print(add(3, 4)) # 7
# Or if __init__.py exports it
from mypackage import add, capitalize_words
print(add(3, 4)) # 7
print(capitalize_words("hello world")) # Hello World
Sub-Packages
Packages can contain other packages:
myproject/
├── main.py
└── mypackage/
├── __init__.py
├── core/
│ ├── __init__.py
│ ├── engine.py
│ └── config.py
├── utils/
│ ├── __init__.py
│ ├── helpers.py
│ └── validators.py
└── models/
├── __init__.py
└── user.py
# Import from sub-packages
from mypackage.core.engine import run
from mypackage.utils.helpers import format_date
from mypackage.models.user import User
Relative Imports Within Packages
Inside a package, use relative imports with dots:
# mypackage/utils/helpers.py
from ..core.config import DATABASE_URL # go up one level, then into core
from .validators import is_valid_email # same directory
| Syntax | Meaning |
|---|---|
from . import module |
Import from the same package |
from .module import name |
Import a name from a sibling module |
from .. import module |
Import from the parent package |
from ..sibling import name |
Import from a sibling package |
Relative imports only work inside packages. They won’t work in scripts run directly.
Modern Python: Implicit Namespace Packages
Since Python 3.3, __init__.py is optional — directories without it are called namespace packages:
mypackage/
├── math_utils.py
└── string_utils.py
# This works even without __init__.py!
from mypackage import math_utils
However, best practice is to always include __init__.py because:
- It’s explicit about what’s a package vs regular directory
- It lets you define package-level exports
- It ensures compatibility with older tools
Real-World Package Layout
Here’s how a professional Python project might be organized:
my-awesome-project/
├── README.md
├── setup.py (or pyproject.toml)
├── requirements.txt
├── tests/
│ ├── __init__.py
│ ├── test_auth.py
│ └── test_users.py
└── src/
└── myapp/
├── __init__.py
├── main.py
├── config.py
├── auth/
│ ├── __init__.py
│ ├── login.py
│ └── tokens.py
├── models/
│ ├── __init__.py
│ └── user.py
└── utils/
├── __init__.py
├── helpers.py
└── validators.py
Summary
- A package is a directory containing Python modules
__init__.pymarks a directory as a package and runs on import- Use
__init__.pyto create convenient package-level exports - Sub-packages are packages inside packages
- Use relative imports (
from . import,from .. import) within packages __init__.pyis optional in Python 3.3+ but recommended- Follow a consistent, well-organized project structure
Next up: Virtual Environments & pip →