Python __init__.py & modular Imports

Python modular import has 2 options,

1. Absolute modular imports
2. Relative modular imports
  • Absolute imports exist from the beginning of python release; however, relative import came once after python 2.7.
  • Relative imports use module’s name attribute to determine the module’s position in the package hierarchy.

A project with multiple hierarchical level directories work as a package with some attributes.

1. There should be a __init__.py file(usually kept empty) in a directory. When a python scripts need to look down into the hierarchical level, the existance of __init__.py file will lead the program flow. Otherwise “package not found error” occurs.

2. The script should be called while running by standing out from the package.

python -m package.subpackage.subsubpackage.pyscript

We can run python scripts in 2 ways.

1.We step into the python script directory and call python mypy.py [Current script is a top-level module]

  • In this case, the import module canot step out of the directory in hierarchical tree.
  • Here code runs on global namespace.

2. We stand ahead of the python package and call python -m myPackage.subPackage.subsubPackage.myScript [modular level module]

  • Here -m tells the modular initialization.
  • We drop .py at the end of script extension.
  • We direct the script to run by the use of namesapces.
  • Namespapces are just the names given to each directory in the package.
  • This script run call tells exactly where the module is actually located inside the package.
  • Here the code runs as a part of an imported module.
  • As this code runs not in global namespace, the __name__ becomes the name of the module.

Namespace for python script :-

Usually, say for example if we have a python script called myPython.py, when we run this script, a namespace will be added to each module, and by default it is __main__.

def funcPy():
pass
if __name__ == ‘__main__’:
print ‘__main__’
else:
print __name__

So basically, python has name module, which holds all namesapces inside. By deafult when a script runs, it grabs the global namespace __main__. We can call this name of the script by __name__ variable. Samelike, we can get package name __package__ and directoty name __dir__. If a script has multiple import and from statements, those modules will take their file name as namesapces, but our primary running script will take __main__ namesapce. However, we can manually change this name. One way is to change in /python2.7/lib/__init__.py folder and other way is to re-initialized inside the code.

By default, __package__ will be assigned to None & __name__ will be assigned to __main__.

******************Don’t Forget*****************

When we use relative modular imports, we shoud stay outside the package and call,

python -m myPackage.subPackage.subsubPackage.myScript

Otherwise, runtime error will rise.

“You’re not using it as a package.”

**********************************************

The __init__.py files are required to make Python treat the directories as containing packages; this is done to prevent directories with a common name, such as string, from unintentionally hiding valid modules that occur deeper on the module search path. In the simplest case, __init__.py can just be an empty file, but it can also execute initialization code for the package or set the __all__ variable, described later.

1. from packagePy import *

2. __all__ = [“module1”, “module2”, module2]

The example for the above implementation is,

PackagePy

1. module0
2. module1
3. module2
4. module3
5. module4

The difference between above 2 is that we don’t have to import all modules inside the package in (2)nd one.

Python searches a list of directories to resolve names while import statements. Because these names can be any directory, or arbitrary names can be added by the developer. However, the developers have to worry about directories that happen to share the same name with a valid Python module, such as ‘string’. To differentiate those 2 modules(default python lib module & user defined module), relative modular import comes into play. The creation of __init__.py will direct the script to search for the namespace in sub directories on hierarchical level of the package. If you remove the __init__.py file, Python will no longer look for submodules inside that directory, so attempts to import the module will fail.

Reference :-

  1. https://stackoverflow.com/questions/37193670/valueerror-attempted-relative-import-in-non-package-not-for-tests-package
  2. https://stackoverflow.com/questions/14132789/relative-imports-for-the-billionth-time