What exactly is the use of __init__.py
? Yes, I know this file makes a directory into an importable package. However, consider the following example:
project/
foo/
__init__.py
a.py
bar/
b.py
If I want to import a
into b
, I have to add following statement:
sys.path.append('/path_to_foo')
import foo.a
This will run successfully with or without __init__.py
. However, if there is not an sys.path.append
statement, a "no module" error will occur, with or without __init__.py
. This makes it seem lik eonly the system path matters, and that __init__.py
does not have any effect.
Why would this import work without __init__.py
?
__init__.py
has nothing to do with whether Python can find your package. You've run your code in such a way that your package isn't on the search path by default, but if you had run it differently or configured your PYTHONPATH
differently, the sys.path.append
would have been unnecessary.
__init__.py
used to be necessary to create a package, and in most cases, you should still provide it. Since Python 3.3, though, a folder without an __init__.py
can be considered part of an implicit namespace package, a feature for splitting a package across multiple directories.
During import processing, the import machinery will continue to
iterate over each directory in the parent path as it does in Python
3.2. While looking for a module or package named "foo", for each directory in the parent path:
- If
<directory>/foo/__init__.py
is found, a regular package is imported and returned.
- If not, but
<directory>/foo.{py,pyc,so,pyd}
is found, a module is imported and returned. The exact list of extension varies by platform
and whether the -O flag is specified. The list here is
representative.
- If not, but
<directory>/foo
is found and is a directory, it is recorded and the scan continues with the next directory in the parent
path.
- Otherwise the scan continues with the next directory in the parent path.
If the scan completes without returning a module or package, and at
least one directory was recorded, then a namespace package is created.
if I want to import a into b, I have to add following statement:
No! You'd just say: import foo.a
. All this is provided you run the entire package at once using python -m main.module
where main.module
is the entry point to your entire application. It imports all other modules, and the modules that import more modules will try to look for them from the root of this project. For instance, foo.bar.c
will import as foo.bar.b
Then it seems that only the system path matters and init.py does not have any effect.
You need to modify sys.path
only when you are importing modules from locations that are not in your project, or the places where python looks for libraries. __init__.py
not only makes a folder look like a package, it also does a few more things like "export" objects to outside world (__all__
)
If you really want to avoid __init__.py
for some reason, you don't sys.path
. Rather, create a module object and set its __path__
to a list of directories.
When you import something it has to either:
- Retrieve an already loaded module or
- Load the module that was imported
When you do import foo
and python finds a folder called foo
in a folder on your sys.path
then it will look in that folder for an __init__.py
to be considered the top level module.
(Note that if the package is not on your sys.path
then you would need to append
it's location to be able to import it.)
If that is not present it will look for a __init__.pyc
version possibly in the __pycache__
folder, if that is also missing then that folder foo
is not considered a loadable python package. If no other options for foo
are found then an ImportError
is raised.
If you try deleting the __init__.pyc
file as well you will see that the the initializer script for a package is indeed necessary.