In this part 2, we will learn more about python package and module.
1. Python packages
In the simplest way, if the module is merely a file ending in .py
, then the package can be considered a directory containing the modules.
Packages are method for managing python’s module namspace using ” dotted module names “. For example, when we type module name AB
, this means we are specifying module B inside package A.
Similar to using modules to help a module writer not to worry about having the same variable / function names in other people’s modules, using dotted module names helps writers to write multi-module packages such as NumPy or Pillow. No need to worry about having the same module name with others.
2. Import
statement
Suppose you want to design a collection of modules (a “package”) to process sound files and sound data. These sound files will come in many different formats (commonly identified by their extensions e.g. .wav
, .aiff
, .au
), so you need to create and maintain a growing collection of modules to convert between different formats.
Next, there are a lot of things you want to do on sound data (e.g. mixing, adding echo, applying equalizer functions, creating sound effects, etc.), so it’s likely that they are. we would have to write an infinitely lengthy series of modules to perform these tasks. Here is a package structure that we can use:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | sound/ Top-level package __init__.py Initialize the sound package formats/ Subpackage for file format conversions __init__.py wavread.py wavwrite.py aiffread.py aiffwrite.py auread.py auwrite.py ... effects/ Subpackage for sound effects __init__.py echo.py surround.py reverse.py ... filters/ Subpackage for filters __init__.py equalizer.py vocoder.py karaoke.py ... |
The __init__.py
file is required in order for Python to treat a directory as a package.
In the simplest case, __init.py__
might just be an empty file, but it can be used to run the initialization code, or to set the value for the __all__
variable, as described later.
Package users can import each module separately, for example:
1 2 3 | <span class="token keyword">import</span> sound <span class="token punctuation">.</span> effects <span class="token punctuation">.</span> echo sound <span class="token punctuation">.</span> effects <span class="token punctuation">.</span> echo <span class="token punctuation">.</span> echofilter <span class="token punctuation">(</span> <span class="token builtin">input</span> <span class="token punctuation">,</span> output <span class="token punctuation">,</span> delay <span class="token operator">=</span> <span class="token number">0.7</span> <span class="token punctuation">,</span> atten <span class="token operator">=</span> <span class="token number">4</span> <span class="token punctuation">)</span> |
Another way to import submodule:
1 2 3 | <span class="token keyword">from</span> sound <span class="token punctuation">.</span> effects <span class="token keyword">import</span> echo echo <span class="token punctuation">.</span> echofilter <span class="token punctuation">(</span> <span class="token builtin">input</span> <span class="token punctuation">,</span> output <span class="token punctuation">,</span> delay <span class="token operator">=</span> <span class="token number">0.7</span> <span class="token punctuation">,</span> atten <span class="token operator">=</span> <span class="token number">4</span> <span class="token punctuation">)</span> |
Or we could go even further, importing the function directly into the symbol table:
1 2 3 | <span class="token keyword">from</span> sound <span class="token punctuation">.</span> effects <span class="token punctuation">.</span> echo <span class="token keyword">import</span> echofilter echofilter <span class="token punctuation">(</span> <span class="token builtin">input</span> <span class="token punctuation">,</span> output <span class="token punctuation">,</span> delay <span class="token operator">=</span> <span class="token number">0.7</span> <span class="token punctuation">,</span> atten <span class="token operator">=</span> <span class="token number">4</span> <span class="token punctuation">)</span> |
Compare from package import item
and import item.subitem.subsubitem
With the from package import item
, the item can be either a submodule, or a subpackage, or it can be a ✔ function, ✔ class or ✔ variable.
In terms of order, the import statement will check if the name is a definition in that package (function, class, variable), if not, it will implicitly this is a module and try to load the module. .
With import item.subitem.subsubitem
, every item except the last, must be a package, the last can be either a module or a package, but it cannot be ❌ function, ❌ class, ❌ variable .
3. Package Relative Imports
Once packages are structured into subpackages (such as a sound
package for example), you will be able to use absolute imports to refer to submodules inside siblings packages .
For example, if the sound.filters.vocoder
module needs to use the echo
module in the sound.effects
package, we could write from sound.effects import echo
.
Or, we can write releative import , still using the form from module import name
. But we will use the leading dots to indicate that the current and parent package are involved in the import process. For example, as we are standing inside the surround
module, we can import the following:
1 2 3 4 5 | <span class="token keyword">from</span> <span class="token punctuation">.</span> <span class="token keyword">import</span> echo <span class="token keyword">from</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token keyword">import</span> formats <span class="token keyword">from</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> filters <span class="token keyword">import</span> equalizer <span class="token keyword">from</span> <span class="token punctuation">.</span> echo <span class="token keyword">import</span> echofilter |
Note: with absolute import , we can use either import <>
or from <> import <>
. But relative import
will only use the form from <> import <>
. The reason is because, with:
1 2 | import XXX.YYY.ZZZ |
XXX.YYY.ZZZ
is a usable expression, but .XXX
is not a valid expression.
4. __all__
variable
There is a question, what happens when a user writes from sound.effects import *
?
Ideally, inside the package’s __init__.py
file, a list of the names of the modules to be imported is provided using the __all__
variable. For example
1 2 | __all__ <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token string">"echo"</span> <span class="token punctuation">,</span> <span class="token string">"surround"</span> <span class="token punctuation">,</span> <span class="token string">"reverse"</span> <span class="token punctuation">]</span> |
Then, the interpreter will only import the modules whose name is provided in this list.
In case the variable __all__
not defined, the statement from sound.effects import *
will NOT import all the submodules of the package sound.effects. It just makes sure that the package sound.effects
is imported, runs all code inside the __init__.py
file, and imports all the names defined in the package. These names include all the functions, classes, and variables defined inside the __init__.py
file and the submodules that are explicitly loaded.
At this point, we will see that the following two ways are equivalent:
1 2 3 4 5 6 7 8 | <span class="token keyword">import</span> sound <span class="token punctuation">.</span> effects <span class="token punctuation">.</span> echo <span class="token keyword">import</span> sound <span class="token punctuation">.</span> effects <span class="token punctuation">.</span> surround <span class="token keyword">import</span> sound <span class="token punctuation">.</span> effects <span class="token punctuation">.</span> reverse Hoặc <span class="token keyword">from</span> sound <span class="token punctuation">.</span> effects <span class="token keyword">import</span> <span class="token operator">*</span> |
Even though both methods produce the same results, import *
still considered bad practice . And from package import specific_submodule
is the recommended way.