PythonPackages

Packages

A package is Python's way of organizing related modules together under a single namespace. While a module is a single .py file, a package is a directory that contains multiple modules (and possibly subpackages), letting you structure larger codebases into logical, importable units.

What Makes a Directory a Package

Traditionally, a directory becomes a package when it contains a special file named __init__.py. This file can be empty, or it can contain initialization code that runs when the package is first imported — for example, setting up package-level variables or re-exporting names from submodules.

Bash
myapp/
├── __init__.py
├── config.py
└── helpers.py

Here, myapp is a package because it has an __init__.py file, and config.py / helpers.py are modules inside it. You could import them like this:

Python
import myapp.config
from myapp.helpers import format_date

myapp.config.load()
format_date('2026-07-06')
Note
Since Python 3.3, directories **without** an `__init__.py` can still act as packages — these are called "implicit namespace packages". Python's import system detects a directory on the module search path and treats it as a package even if it has no `__init__.py`. However, most real-world projects still add an explicit `__init__.py` because it gives you a clear place to control initialization, define `__all__`, and make the package's intent unambiguous.
A Larger Package Structure

Real applications usually nest packages inside packages. Here's a more realistic layout for a small app with a data layer and utilities:

Bash
myapp/
├── __init__.py
├── utils.py
├── models/
│   ├── __init__.py
│   ├── user.py
│   └── product.py
└── services/
    ├── __init__.py
    └── billing.py

models and services are subpackages of myapp — each is its own directory with its own __init__.py, nested inside the parent package's directory.

Subpackages and Dotted Imports

To reach a module inside a subpackage, you chain names together with dots, following the directory path exactly:

Python
from myapp.models.user import User
from myapp.models import product
from myapp.services.billing import charge_card

user = User(name='Ada')
charge_card(user, amount=1999)

Each level of nesting (myappmodelsuser) corresponds to a directory or file on disk, and each directory along the way needs its own __init__.py (unless you're relying on implicit namespace packages).

Absolute Imports Within a Package

Inside myapp/services/billing.py, you generally want to reference other parts of the package using absolute imports — the full dotted path starting from the top-level package name, exactly as an external caller would write it:

Python
# myapp/services/billing.py

from myapp.models.user import User
from myapp.utils import format_currency

def charge_card(user: User, amount: int) -> str:
    return f'Charged {user.name}: {format_currency(amount)}'

Absolute imports are unambiguous and easy to read no matter where the file lives. Python's import system resolves myapp.models.user the same way whether it's imported from billing.py, from a test file, or from an interactive shell — as long as myapp's parent directory is on sys.path. (Relative imports, using leading dots like from .user import User, are a related tool covered in depth on the import-system page.)

Controlling Wildcard Imports with __all__

When someone writes from myapp import *, Python needs to decide which names get pulled in. By default, it imports every top-level name that doesn't start with an underscore. You can take explicit control of this with the __all__ list in __init__.py:

Python
# myapp/__init__.py

from myapp.utils import format_currency, format_date
from myapp.models.user import User

__all__ = ['format_currency', 'format_date', 'User']

Now from myapp import * will only bring in format_currency, format_date, and User — nothing else, even if other names exist in the module's namespace. This keeps the package's public surface intentional and predictable.

Warning
Using `from package import *` in application code is generally considered an anti-pattern — it pollutes the local namespace with names you can't immediately trace back to their source, making code harder to read and increasing the risk of silent name collisions. Prefer explicit imports like `from myapp.utils import format_currency`. Defining `__all__` is still worthwhile because it documents your package's public API and keeps wildcard imports safe for the rare cases they're used (e.g. in interactive sessions).
Tip
Keep `__init__.py` files minimal. It's tempting to put lots of logic there, but a thin `__init__.py` that only re-exports a few key names (as in the example above) keeps import time fast and avoids circular-import headaches as the package grows. Heavy setup logic belongs in a dedicated module that `__init__.py` imports from.
Why Bother with Packages?
  • Packages let you group related modules under one namespace instead of dumping dozens of flat .py files in a single directory.

  • Subpackages mirror your application's domains (models, services, utils) so the file layout documents the architecture.

  • An __init__.py gives you a stable place to define a package's public API via imports and __all__, independent of how the internals are split across files.

  • Namespace packages (no __init__.py) are mostly useful for splitting a single logical package across multiple distributions — most everyday projects are better served by explicit packages.