The Python __init__.py
file serves two main functions:
-
It is used to label a directory as a python package to make it visible so other python files can re-use the nested resources (e.g. the
incr
method defined insidehelpers/file1.py
):from helpers.file1 import incr result = incr(42) assert result == 43
A side effect is that – with some not-recommended workarounds – developers do not have to care about the method’s location in your package hierarchy:
helpers/ ├── __init__.py ├── file1.py ├── file2.py ├── ... └── fileN.py
For that, simply fill the
__init__.py
file with the following content:from file1 import * from file2 import * ... from fileN import *
Therefore, even though it is always a good practice to explicitely mention the source, they can simply use:
from helpers import incr result = incr(42) assert result == 43
-
It is used to define variables or to initialise objects like
logging
at the package level and import time (to make them accesible at a global package level):from helpers.file3 import MY_VAR print(MY_VAR)
Still blur? Thereafter an easy example to understand:
First, let’s plot some context
You have the following project structure:
playground_packages
├── helpers/
└── utils.py
└── main.py
The utils.py
file contains:
def incr(n:list[float]) -> list[float]:
return [x+1 for x in n]
if __name__ == "__main__":
pass
Note: you could have also used the map
and lambda
methods instead. However, here is a nice example to show about list comprehension. The alternave version would have looked like:
list(map(lambda x: x+1, n))
The main.py
file is looking like the following:
from helpers.utils import incr
def main() -> None:
result = incr([1,2,3,4,5])
print(result)
if __name__ == "__main__":
main()
Notes:
- Why we haven’t used
import helpers.utils
orimport *
is explained here (to do). - The
if __name__ == "__main__"
conditional statement is explained here (to do).
__init__.py
to label a folder as Python package
Jumping back to our example, if you try to run the code with the current configuration, you will get the following error:
> python main.py
Traceback (most recent call last):
File "path/to/playground_package/main.py", line 1, in <module>
from helpers.utils import incr
ModuleNotFoundError: No module named 'helpers'
This is because the helpers
directory is not yet visible for Python. Python is actively looking for Python packages but cannot find any. A package is a folder that contains a __init__.py
file.
Simply edit our current structure for the following:
playground_packages
├── helpers/
├── __init__.py
└── utils.py
└── main.py
Now, it you try again, it will succeed:
> python main.py
[2, 3, 4, 5, 6]
The main take-away is:
If you want to split-up your code in different folders and files (to make your code more readable and debuggable), you must create a __init__.py
file under each folder so they become visible for Python and can therefore be used and refered to in your code using import
.
__init__.py
to define global variables
In our previous example, the __init__.py
file is empty. We can edit it, adding the following line:
MY_LIST = [2,4,6,8,10]
This variable is accessible even by the main function:
from helpers import MY_LIST
from helpers.utils import incr
def main() -> None:
result = incr(MY_LIST)
print(result)
if __name__ == "__main__":
main()
> python main.py
[3, 5, 7, 9, 11]
Note: it is better to define variables in a config.py
or constants.py
file rather than in a __init__.py
file. However, __init__.py
becomes handy when it comes to instanciate objects such as logging
or dynaconf
. More on that will follow in another article.
You are now ready to fit your code together like Russian dolls 🪆