Python Basics — Packages & init.py

2 minute read

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:

  1. Marks the directory as a Python package (required before Python 3.3)
  2. 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__.py marks a directory as a package and runs on import
  • Use __init__.py to create convenient package-level exports
  • Sub-packages are packages inside packages
  • Use relative imports (from . import, from .. import) within packages
  • __init__.py is optional in Python 3.3+ but recommended
  • Follow a consistent, well-organized project structure

Next up: Virtual Environments & pip →

Updated: