I use __init__.py
to run checks when I do from myprojects.something import blabla
.
Today I started using pyzmq
and I wanted to see what's going on behind the scenes. So I browsed the code in github and I find (for me) some strange usage of __init__.py
there that I cannot explain myself.
For example zmq/core/__init__.py
. What's the point of adding in zmq.core.__all__
the __all__
's value of zmq.core.constants, zmq.core.error, zmq.core.message, etc.
?
In zmq/__init__.py
I see at the end
__all__ = ['get_includes'] + core.__all__
where get_includes
is a function which basically returns a list with the directory of the module and the utils directory in the parent directory.
What's the point of that? What has __init.py__
achieved by doing that?
The __init__.py files are required to make Python treat directories containing the file as packages. This prevents directories with a common name, such as string , unintentionally hiding valid modules that occur later on the module search path.
The __init__.py file makes Python treat directories containing it as modules. Furthermore, this is the first file to be loaded in a module, so you can use it to execute code that you want to run each time a module is loaded, or specify the submodules to be exported.
Starting with Python 3.3, Implicit Namespace Packages were introduced. These allow for the creation of a package without any __init__.py file. Of course, it can still be present if package initialization is needed. But it is no longer required.
It provides an easy way for you to group large folders of many separate python scripts into a single importable module. 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.
The __all__
is for when someone does from module import *
as documented here.
The only solution is for the package author to provide an explicit index of the package. The import statement uses the following convention: if a package’s
__init__.py
code defines a list named__all__
, it is taken to be the list of module names that should be imported when from package import * is encountered. It is up to the package author to keep this list up-to-date when a new version of the package is released. Package authors may also decide not to support it, if they don’t see a use for importing * from their package. For example, the filesounds/effects/__init__.py
could contain the following code:
__all__ = ["echo", "surround", "reverse"]
This would mean that
from sound.effects import *
would import the three named submodules of the sound package.
One use for __all__
is a tool for package builders to allow them to structure their package in a way that works for them while making it convenient for users. Specifically in the case of pyzmq, it lets you write code such as:
import zmq
print zmq.zmq_version()
Rather than having to use the full dotted module name:
print zmq.core.version.zmq_version()
The package designers of pyzmq are using __all__
to promote namespace elements from nested modules up to the top level of their namespace so the user isn't bothered by the structure of their package.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With