Question

What __init__.py Is For in Python Packages

pythonmodulepackagepython-packaging

Question

In Python, what is the purpose of an __init__.py file inside a source directory? How does it affect packages, imports, and module organization?

Short Answer

By the end of this page, you will understand what __init__.py is, why it is used in Python packages, and how it can control imports, package setup, and code organization. You will also see common patterns, mistakes, and practical examples.

Concept

In Python, an __init__.py file is associated with packages.

A package is a directory that groups related Python modules together. Historically, placing an __init__.py file inside a directory told Python, "this directory should be treated as a package."

What __init__.py does

Depending on how it is used, __init__.py can serve several purposes:

  • Marks a directory as a package in traditional Python packaging.
  • Runs initialization code when the package is imported.
  • Controls what gets exposed when users import from the package.
  • Makes package-level imports cleaner by re-exporting names from submodules.

For example, suppose you have this structure:

myapp/
    __init__.py
    utils.py
    config.py

Then Python can import from it like this:

import myapp
from myapp import utils

Why it matters

__init__.py helps organize code into logical units. In real projects, developers often use it to:

  • group related modules
  • define a clean public API for a package
  • avoid forcing users to import from deep internal files
  • perform lightweight package setup

Important modern note

In modern Python, some directories can work as namespace packages even without __init__.py. So the file is no longer always required just to make imports work.

However, __init__.py is still very common and useful when you want package-level behavior, explicit structure, or controlled exports.

Mental Model

Think of a package directory like a store, and __init__.py like the front desk.

  • The directory contains all the departments: utils.py, config.py, models.py
  • The __init__.py file is the place where the store introduces itself
  • It can decide what customers see first
  • It can hand out shortcuts to important items
  • It can do a small amount of setup when someone enters

So instead of forcing users to walk through every aisle manually, __init__.py can guide them to the most useful parts of the package.

Take Quiz

Syntax and Examples

Basic package structure

mypackage/
    __init__.py
    math_tools.py

math_tools.py:

def add(a, b):
    return a + b

__init__.py:

from .math_tools import add

Now a user can write:

from mypackage import add

print(add(2, 3))

Instead of:

from mypackage.math_tools import add

Running code when a package is imported

__init__.py runs when the package is imported:

print("mypackage was imported")

Then:

import mypackage

will print:

mypackage was imported

This is possible, but in real code, initialization should usually stay lightweight.

Defining package metadata

A package may also store simple package-level values in __init__.py:

__version__ = "1.0.0"

Then:

import mypackage
print(mypackage.__version__)

Controlling from package import *

You can define __all__:

__all__ = ["add"]

This controls what gets imported by wildcard imports, though wildcard imports are generally avoided in most codebases.

Step by Step Execution

Consider this package:

tools/
    __init__.py
    helpers.py

helpers.py:

def greet(name):
    return f"Hello, {name}!"

__init__.py:

from .helpers import greet
print("tools package initialized")

Now run:

import tools
print(tools.greet("Ava"))

What happens step by step

  1. Python sees import tools.

  2. It looks for the tools package.

  3. It loads and executes tools/__init__.py.

  4. Inside __init__.py, Python runs:

    from .helpers import greet
    

    This imports greet from helpers.py into the package namespace.

  5. Python then runs:

    print("tools package initialized")
    
  6. The package is now available as tools.

  7. Because greet was imported into the package namespace, tools.greet("Ava") works.

  8. The final output is:

tools package initialized
Hello, Ava!

This shows the two key ideas:

  • __init__.py runs at import time
  • it can expose selected names at the package level

Real World Use Cases

1. Creating clean import paths

Instead of this:

from mylibrary.formatters.text import format_name

A package can expose a simpler import:

from mylibrary import format_name

2. Grouping related code

Projects often organize code into packages such as:

  • models
  • services
  • utils
  • api
  • tests

Each package may use __init__.py to define what belongs in its public API.

3. Package-level constants or metadata

A library might define:

__version__ = "2.3.1"

or configuration defaults in __init__.py.

4. Small startup behavior

Some packages do lightweight setup during import, such as:

  • initializing a logger
  • importing commonly used classes
  • setting package-level aliases

5. Framework and library organization

Large Python codebases often use __init__.py files in many directories so modules are clearly grouped and import paths stay predictable.

Take Quiz

Real Codebase Usage

In real projects, developers usually keep __init__.py simple and intentional.

Common patterns

Re-exporting useful names

A package may expose the most important classes or functions:

from .client import APIClient
from .errors import APIError

This lets users write:

from mypackage import APIClient, APIError

Defining package metadata

__version__ = "0.4.0"

Creating a public API boundary

A project might contain many internal modules, but only a few should be imported directly. __init__.py helps define that boundary.

Keeping imports stable

If internal files move later, the package can still expose the same top-level names through __init__.py, reducing breaking changes.

What developers avoid

Heavy import-time work

Avoid putting expensive logic in __init__.py, such as:

  • database connections
  • network requests
  • long file processing

That makes imports slow and harder to reason about.

Circular imports

Because __init__.py often imports submodules, it can accidentally create circular import problems if modules also import back into the package.

Too much hidden magic

If __init__.py does too much, the package becomes harder to understand and debug.

Common Mistakes

1. Thinking __init__.py is always required

In older Python usage, it was required to make a directory a package. In modern Python, some packages can work without it as namespace packages.

Still, if you want package initialization or explicit exports, you should include it.

2. Putting too much code inside it

Broken idea:

# __init__.py
connect_to_database()
load_large_dataset()
start_background_worker()

Why this is a problem:

  • importing the package becomes slow
  • imports may have side effects
  • testing becomes harder

Better:

# __init__.py
from .client import APIClient

Move expensive actions into functions that run only when needed.

3. Causing circular imports

Broken example:

# __init__.py
from .module_a import thing_a

# module_a.py
from mypackage import thing_b

This can fail because the package is not fully initialized yet.

Avoid this by:

  • reducing cross-imports
  • moving shared code into a separate module
  • importing locally inside functions if necessary

4. Expecting wildcard imports to be ideal

This:

from mypackage import *

can make code unclear. Prefer explicit imports.

5. Forgetting relative import syntax inside packages

Inside __init__.py, this is common:

from .helpers import greet

The leading dot means "from this package." Beginners sometimes forget the dot and get import errors.

Comparisons

__init__.py vs no __init__.py

SituationWith __init__.pyWithout __init__.py
Traditional packageYesNo
Can run package setup codeYesNo
Can re-export namesYesNo
Namespace package supportNot the main purposePossible in modern Python
Explicit package structureClearLess explicit

Package vs module

ConceptMeaning
ModuleA single .py file
PackageA directory containing related modules, often with __init__.py

Importing from submodule vs package

StyleExampleWhen useful
Direct submodule importfrom mypackage.helpers import greetClear when you want a specific internal module
Package-level importfrom mypackage import greetCleaner API for package users

Relative import vs absolute import

StyleExampleNotes
Relative importfrom .helpers import greetCommon inside packages
Absolute importfrom mypackage.helpers import greetClearer from outside the package
Take Quiz

Cheat Sheet

Quick reference

What is __init__.py?

  • A special file used in Python packages
  • Runs when the package is imported
  • Can expose names at the package level
  • Can hold package metadata

Common uses

from .helpers import greet
__version__ = "1.0.0"
__all__ = ["greet"]

Typical package structure

mypackage/
    __init__.py
    helpers.py
    config.py

Import behavior

import mypackage
from mypackage import greet

Best practices

  • Keep __init__.py small
  • Re-export only important names
  • Avoid heavy side effects
  • Use relative imports inside the package when appropriate

Watch out for

  • circular imports
  • slow import-time code
  • overusing wildcard imports
  • assuming it is always required in modern Python

FAQ

What does __init__.py do in Python?

It marks or defines package behavior, runs package initialization code, and can expose selected names when the package is imported.

Is __init__.py required in every Python package?

Not always. Modern Python supports namespace packages without it. But it is still useful when you want explicit package behavior or cleaner imports.

When is __init__.py executed?

It is executed when the package is imported for the first time in a Python process.

Why would I put imports inside __init__.py?

To re-export commonly used classes or functions so users can import them from the package directly.

Can __init__.py be empty?

Yes. An empty __init__.py is valid and still useful for explicit package structure.

Should I put application logic in __init__.py?

Usually no. Keep it lightweight. Avoid expensive work or side effects during import.

What is the difference between __init__.py and __main__.py?

__init__.py is for package initialization. __main__.py is for running a package as a script, such as with python -m package_name.

Take Quiz

Mini Project

Description

Build a small Python package called shop that groups related code and exposes a clean package-level API through __init__.py. This demonstrates how packages are organized and how __init__.py can make imports simpler.

Goal

Create a package where users can import package-level functions directly instead of importing from internal modules.

Requirements

[ "Create a package directory named shop with an __init__.py file.", "Add one module for pricing logic and one module for formatting text.", "Use __init__.py to expose one function from each module at the package level.", "Write a small script that imports from shop and uses those functions." ]

Take Quiz

Keep learning

Related questions

View more