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__.pyfile 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.
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")
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
-
Python sees
import tools. -
It looks for the
toolspackage. -
It loads and executes
tools/__init__.py. -
Inside
__init__.py, Python runs:
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:
modelsservicesutilsapitests
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:
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:
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
Comparisons
__init__.py vs no __init__.py
| Situation | With __init__.py | Without __init__.py |
|---|---|---|
| Traditional package | Yes | No |
| Can run package setup code | Yes | No |
| Can re-export names | Yes | No |
| Namespace package support | Not the main purpose | Possible in modern Python |
| Explicit package structure | Clear | Less explicit |
Package vs module
| Concept | Meaning |
|---|
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__.pysmall - 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 .
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."
]
Keep learning
Related questions
@staticmethod vs @classmethod in Python Explained
Learn the difference between @staticmethod and @classmethod in Python with clear examples, use cases, mistakes, and a mini project.
Catch Multiple Exceptions in One except Block in Python
Learn how to catch multiple exceptions in one Python except block using tuples, with examples, mistakes, and real-world usage.
Convert Bytes to String in Python 3
Learn how to convert bytes to str in Python 3 using decode(), text mode, and proper encodings with practical examples.