Chapter 8: Python packages

The __init__.py file

So why do you still need __init__.py then?

Turning a directory into a module

__init__.py can actually turn a directory name into a module. So, you can now import sound as if there were a sound.py file. The file __init__.py is basically a replacement for sound.py.

sound/
    __init__.py
    formats/
        __init__.py
        wavread.py
        wavwrite.py
        ...
    effects/
        __init__.py
        echo.py
        surround.py
        ...
    filters/
        __init__.py
        equalizer.py
        vocoder.py
        ...

What this means is that you can put anything inside __init__.py, and it will run when you import sound. Similarly, if you write some code in sound/__init__.py and sound/format/__init__.py that prints out something, then both files will be executed when you import sound.formats. Try this!

Including metadata

You can also include a docstring in the first line of sound/__init__.py, and this will print out when you type sound.__doc__ after you import sound.

It’s also quite common to include dunder variables like the following at the beginning of an __init__.py file (and even at the beginning of a normal module file).

__version__ = "0.8"
__author__ = "Josiah Wang"

The __all__ magic variable

The biggest use for __init__.py is to set the __all__ magic variable. This will allow you to use the wildcard from sound.formats import *.

You can try this. First try the following without adding anything to sound/formats/__init__.py. Neither wavread nor wavwrite will be imported.

>>> from sound.formats import *
>>> wavread
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'wavread' is not defined
>>> dir() # this shows what variables are currently available in the namespace
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']

Then add the following line to sound/format/__init__.py.

__all__ = ["wavread", "wavwrite"]

If you run the following again, it should now successfully import both wavread and wavwrite (you may need to restart the interpreter to recompile the package).

>>> from sound.formats import *
>>> wavread
<module 'sound.formats.wavread' from '/xxx/xxx/xxx/sound/formats/wavread.py'>
>>> wavwrite
<module 'sound.formats.wavwrite' from '/xxx/xxx/xxx/sound/formats/wavwrite.py'>
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'wavread', 'wavwrite']

That’s as far as we will cover for packages! You can reorganise your robot project files into packages once it has grown larger, but this is just a bonus task for you to explore on your own.