How do I write good/correct package __init__.py files
Data Structures & Algorithms practice on Codemia
Step through 300+ coding interview problems with animated visualisers that show the data structure changing as the code runs. The Blind 75 are free.
Introduction
An __init__.py file marks a directory as a Python package and controls what gets imported when someone uses from mypackage import .... A good __init__.py does three things: defines __all__ to control from package import *, re-exports the public API for convenient access, and stays minimal (no heavy computation). In Python 3.3+, __init__.py is optional for namespace packages, but explicit packages with __init__.py are still the standard for most projects.
Minimal init.py
The simplest valid __init__.py is an empty file:
Re-Exporting the Public API
A well-designed __init__.py re-exports key symbols so users do not need to know the internal module structure:
Defining all
__all__ controls what from package import * exports:
Without __all__, from package import * exports everything that does not start with _.
Package with Subpackages
Lazy Imports for Performance
If your package has expensive imports, defer them:
This pattern (PEP 562) works in Python 3.7+.
Version and Metadata
Real-World Examples
Flask-style
requests-style
What NOT to Put in init.py
Common Pitfalls
- Importing everything with
from submodule import *in__init__.py: This pollutes the package namespace with every symbol from every submodule. Name collisions are silent — a later import overwrites an earlier one. Import only the public API explicitly. - Running expensive code at import time: Database connections, file reads, or network requests in
__init__.pyslow down every import of the package. Use lazy imports with__getattr__or move initialization to an explicitinit()function. - Circular imports between
__init__.pyand submodules: If__init__.pyimports frommodule_a, andmodule_aimports frommypackage(which triggers__init__.pyagain), you getImportErroror partially initialized modules. Break the cycle by importing at function scope or restructuring. - Not defining
__all__: Without__all__,from package import *exports everything in the namespace, including re-imported standard library modules and private helpers. Always define__all__to control the public API. - Creating a namespace conflict with the package name: If
mypackage/__init__.pydefinesmypackage = "something", it shadows the package itself. Avoid defining variables or functions with the same name as the package.
Summary
- Use
__init__.pyto define the package's public API by re-exporting key symbols - Always define
__all__to controlfrom package import *behavior - Keep
__init__.pyminimal — no heavy computation, no side effects - Use
__getattr__(Python 3.7+) for lazy imports of expensive modules - Import specific names, never
from submodule import *, to avoid namespace pollution
Related reading
- How do I write JSON data to a file?
- How do Python dictionary hash lookups work?
- How do Python functions handle the types of parameters that you pass in?
- How do Python's any and all functions work?
- How do threads work in Python, and what are common Python-threading specific pitfalls?
- How do we determine the number of days for a given month in python
- How do you access the query string in Flask routes?
- How do you add a Dictionary of items into another Dictionary
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseNo course covers this one yet
Basis Lab writes one for you from a sentence about what you want to be able to do, then teaches it and checks you understood. Your first course is free.
Build my courseData Structures & Algorithms practice on Codemia
Step through 300+ coding interview problems with animated visualisers that show the data structure changing as the code runs. The Blind 75 are free.