7 Working with modules and packages
As we have seen in Chapter 5 and Chapter 6 it makes sense to pack functionality together that belongs together. In Python this is done via modules and packages.
From the Python glossary we get:
An object that serves as an organizational unit of Python code. Modules have a namespace containing arbitrary Python objects. Modules are loaded into Python by the process of importing.
See also package.
A Python module which can contain submodules or recursively, subpackages. Technically, a package is a Python module with a path attribute.
See also regular package and namespace package.
We use the same example here as later on in Chapter 13.
We define a class Rectangle with some functions and in addition we include a function is_square in the file but not the class. Everything together it looks like
and we store it in the root directory of our project as rectangle.py.
Now, if we call Python we can import the module via its name rectangle (the extension is not used)
import rectangle
rect = rectangle.Rectangle(1, 1)
print(f"{rect.get_area() = }")
print(f"{rectangle.is_square(rect) = }")rect.get_area() = 1
rectangle.is_square(rect) = True
We can also get a specific resource and we can rename it
from rectangle import is_square as check_square
rect = rectangle.Rectangle(1, 1)
print(f"{check_square(rect) = }")check_square(rect) = True
or everything
from rectangle import *
rect = Rectangle(1, 1)
print(f"{is_square(rect) = }")is_square(rect) = True
We can see in the last two examples, that we no longer need to specify the module, i.e. the namespace, to access.
We can also execute the module, which will result in not anything happening for our original file but if we add 1
if __name__ == "__main__":
import sys
r = Rectangle(int(sys.argv[1]), int(sys.argv[2]))
print(f"{r.get_area() = }")at the end we can call it and we get a result:
python rectangle.py 1 2 r.get_area() = 2
Now, depending on the input provided the area of the rectangle is computed.
When including or calling a module, Python needs to know where the module is located. This is realised via the module search path. When calling import the following locations are processed:
- The build in modules.
- The directory containing the input script or file, most of the time this is the current directory
- The environment variable
PYTHONPATH(a list of directory names, with the same syntax as the shell variablePATH). - The installation-dependent default (by convention including a
site-packagesdirectory, handled by thesitemodule), we usepdmto manage these in.venv.
Consequently, if we want to include something in subdirectory we can use src.rectangle where . replaces the path separator /.
We can see this in action when using packages, as we know a collection of modules. For example, numpy or pandas are packages, that contain various modules and we discuss them in the next section.
In order to create a package the __init__.py file needs to be part of a directory, to let Python know that this is a package. As example we have a look at our toy example module again.
$ tree module/
module/
├── pdm.lock
├── pyproject.toml
├── src
1 └── rectangle
2 ├── __init__.py
3 └── rectangle.py- 1
-
The directory containing the module, in our case
src.rectangle. - 2
- The init file.
- 3
- The actual code.
We have two such files where for the module part the content is.
from .rectangle import *This explicitly uses the current directory and imports everything into the namespace of the module. Therefore, we do not need to write rectangle.rectangle.Rectangle to use the Rectangle class but just rectangle.Rectangle.
This concludes our short introduction to modules, for further details we suggest the following references.
7.1 References
We only provide a glimpse into the concept of functional programming in Python. There are more guides out there, like