Python Modules and Packages

Python Modules and Packages

When you start writing more complex code or creating classes and functions that you use all the time, it makes sense to have that code inside a Python module. If you have been following my blog, there are two modules that I created that I use quite often in the examples throughout the posts: gpiozero_extended.py and utils.py. The idea of this post is to give a higher level explanation using some diagrams and simple code. A very complete explanation can be found in the official Python documentation for Modules, from which I quote:

Python has a way to put definitions in a file and use them in a script or in an interactive instance of the interpreter. Such a file is called a module; definitions from a module can be imported into other modules or into the main module.

Furthermore, you can group related modules into a Python package. In general terms, packages reside in your computer as a folder structure with a special file in them. The illustration below shows what a few packages would look like in a folder structure. Notice the __init__.py files in each package folder. Those are required for Python to treat the directories as packages. In the simplest scenario, it can be an empty file. It can also contain initialization code for the package.

Inside each package folder, in addition to the __init__.py file, there are the module files. In the case shown above, package_1 contains module_a.py and module_b.py. Each module can contain any number of classes and/or functions, as well as initialization code.

Let’s create some fictitious modules for package_1 and package_2 containing some empty classes and functions. On a Windows platform, the package folders can be created where your Python applications are kept. Let’s say the folders:

  • C:\Users\myusername\python\mypackages\package_1
  • C:\Users\myusername\python\mypackages\package_2

Just like shown in the illustration above, make sure each package folder has an empty __init__.py file and the following modules files:

"""
This is module_a.py located in package_1.

"""
# Eventual initialization code


# Classes
class MyClassA:
    pass

class MyClassB:
    pass


# Functions
def myfunction_a():
    pass

def myfunction_b():
    pass
"""
This is module_b.py located in package_1.

"""
# Eventual initialization code


# Classes
class MyClassC:
    pass

class MyClassD:
    pass


# Functions
def myfunction_a():
    pass

def myfunction_c():
    pass
"""
This is module_a.py located in package_2.

"""
# Eventual initialization code


# Classes
class MyClassE:
    pass

class MyClassF:
    pass


# Functions
def myfunction_e():
    pass

def myfunction_f():
    pass
"""
This is module_c.py located in package_2.

"""
# Eventual initialization code


# Classes
class MyClassG:
    pass

class MyClassH:
    pass


# Functions
def myfunction_g():
    pass

def myfunction_h():
    pass

PYTHONPATH

Before we can start importing modules and their components, we have to make sure that the mypackages folder can be found by the Python interpreter. One good way to ensure that that is always the case is by adding it to the PYTHONPATH environment variable.

On Windows platforms open the environment variables editor using the search bar. Then, choose the Environment Variables button to access the lists of both user and system variables.

Under the System variables, if there isn’t already a PYTHONPATH variable, create a new one as shown next. Otherwise, you can add the package path to the existing one (separated by a semi-colon).

Importing Classes and Functions

The most elegant way to import classes and functions from a package is by using the dot notation to go directly to the desired component. Once the mypackages folder has been added to the PYTHONPATH, you can try the following imports:

>>> from package_1.module_a import MyClassA
>>> from package_2.module_a import MyClassE

As long as the components being imported have distinct names (MyClassA and MyClassE), different packages can have modules with the same name. As discussed in Classes vs Functions, multiple instances of each class can then be created:

>>> instanceA0 = MyClassA()
>>> instanceA1 = MyClassA()
>>> instanceE0 = MyClassE()

While not customary with classes, sometimes functions are imported under a different (more convenient) name:

>>> from package_2.module_a import myfunction_e as fe
>>> from package_2.module_c import myfunction_g as fg

More on Python Packages

What about the packages that we can install using pip (preferred installer program)? They are also a collection of modules that can help us solve specific problems, neatly organized in self contained distributable packages.

In my case, since I have administrator rights on a Windows platform running Python 3.9, pip installed packages end up in the folder C:\Program Files\Python39\Lib\site-packages. That location is automatically on the Python search path and doesn’t need to be in the PYTHONPATH environment variable. Modules in those packages (such as numpy or scipy) are readily available for imports into your code.

These more professional packages can be found in the PyPi (Python Package Index) public repository. In fact, anyone can create a package and upload it to the PyPi repository. As usual, there is great official documentation on how to distribute your own python modules here. Although the process can be somewhat involved, some time ago I created my own packages (pyev3 and labjack-unified) and added them to the repository!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s