Programming in Python

Modules and Packages

Gerald Senarclens de Grancy

Modular Programming

Emphasizes breaking down a program into smaller, manageable modules

Modules should be self-contained (minimal dependencies on other modules)

  • Functionality is grouped in interchangeable modules
  • Modules are designed to have a specific responsibility within the system
  • They provide an interface usable by other modules

Which Problems does Modular Programming Solve?

  • Modules also allow code-reuse between programs
  • Independent modules are easier to test
  • Maintenance is facilitated
  • No copy and paste between programs
  • Input/ UI, database and computations should not be mixed
  • Facilitates specialization and division of labor

Which Problems does Modular Programming Cause?

  • Tooling has to support working with multiple files
    eg. search and replace in multiple files
  • Importing modules can be tricky using relative imports
  • Splitting an application well requires experience

Python Modules

A module is a file containing Python definitions and statements.
Every *.py file is a module

  • A program can contain many modules (*.py files)
  • Modules (*.py files) can be imported or executed

If a program consists of a single file,
that file is a module as well as a program

Independent functionality should reside in different modules

Python Modules

Code in one module can be accessed from other modules

import name  # this imports the code in `name.py`
from name import object  # this imports only one object defined in `name.py`

Modules provide namespaces (avoid name clashes)

import math

def sin(x):
    return f'this function does not affect sin({x}) from the math module'

print(sin(math.pi))
print(math.sin(math.pi))

Visualize execution

Python Modules - Example

A solver for quadratic equations that can be used by other modules

import cmath
import math

def quadratic(a, b, c):
    """
    Compute the solution to a quadratic equation.
    """
    root = b ** 2 - 4 * a * c
    if root >= 0:
        sqrt = math.sqrt
    else:
        sqrt = cmath.sqrt
    x1 = (-b + sqrt(root)) / (2 * a)
    x2 = (-b - sqrt(root)) / (2 * a)
    return x1, x2
Download solver.py

Python Modules - Example

A simple GUI using the above solver

#!/usr/bin/env python3
import tkinter as tk  # requires having package `tk` installed
from solver import quadratic
def click_solve() -> None:
    """Compute and print the roots of a quadratic equation."""
    x1, x2 = quadratic(a.get(), b.get(), c.get())
    print('x1:', x1, 'x2:', x2)

app = tk.Tk()
a, b, c = tk.DoubleVar(), tk.DoubleVar(), tk.DoubleVar()
app.title('Solver for Quadratic Equations')
inputs = {' x^2': a, ' x + ': b, ' = 0': c}
for text, var in inputs.items():
    tk.Entry(app, width=3, textvariable=var).pack(side=tk.LEFT, pady=10)
    tk.Label(text=text).pack(side=tk.LEFT, pady=10)
tk.Button(app, text='solve', command=click_solve).pack(side=tk.LEFT, padx=10)
app.mainloop()
Download ui.pyw

Python Modules - Example

A simple CLI using the above solver

#!/usr/bin/env python3
"""
Command-line interface for solving quadratic equations.
"""
import sys
from solver import quadratic

def main():
    a = float(input("Enter the value for a: "))
    b = float(input("Enter the value for b: "))
    c = float(input("Enter the value for c: "))
    x1, x2 = quadratic(a, b, c)
    print('x1:', x1, 'x2:', x2)
    return 0

if __name__ == '__main__':
    sys.exit(main())
Download cli.py

How are modules found?

The interpreter first searches for built-in modules

When there is no built-in module matching the name, it searches for a matching filename in a list of directories: sys.path

To include additional directories in sys.path on every startup, add them to the PYTHONPATH environment variable

To initialize PYTHONPATH automatically, modify your ~/.profile
(in most Linux distributions)

ifmain Pattern

The code in a module is executed whenever it is imported

If different behavior is required when a program is executed,
developers can use the special __name__ variable

This variable is set by the Python interpreter:

  • In imported code, __name__, is set to the name of the imported module
  • In executed scripts __name__, is set to '__main__'

The ifmain pattern allows to distinguish if code is imported or executed

#!/usr/bin/env python3

print(f'{__name__=}')

if __name__ == "__main__":  # False if this module is imported
    print("executed as program")
Download ifmain.py

Status Codes

Programs indicate to the OS whether or not their execution was successful

In Python, this is done explicitly by calling sys.exit(.)

Status code 0 indicates success, all other codes indicate failure

This is usually combined with the ifmain pattern

#!/usr/bin/env python3
import sys

# lots of code here

if __name__ == "__main__":  # False if this module is imported
    print("executed as program")
    sys.exit(0)  # let the OS know that everything went fine
Download ifmain_status.py

Code that should only run when a script is executed is best placed in the main() function

The following example shows the complete ifmain pattern

#!/usr/bin/env python3
import sys

# lots of code here
def main():
    print("executed as program")
    return 0  # success

if __name__ == "__main__":  # False if this module is imported
    sys.exit(main())  # OS gets return value of main()
Download ifmain_complete.py

Below is a possible template for executable scripts

#!/usr/bin/env python
"""
.. module:: example
    :synopsis: One sentence describing what your `example` module does
.. moduleauthor:: Gerald Senarclens de Grancy <oss@senarclens.eu>

Detailed description of what the module can do.
"""
# standard library imports first (sorted alphabetically)
import os
import sys
# third party library imports next
import numpy
# finally import own modules

def main():
    return 0

if __name__ == "__main__":
    sys.exit(main())

# meta information for documentation purposes
__copyright__ = '2024, Gerald Senarclens de Grancy'
__license__ = 'GPLv3'
Download template.py

Packages

Python programs and libraries are installed from packages

Packages are a means of publishing related modules as program or library

Python Package Index (PyPI)
official third-party software repository for Python
pypi.python.org

Installing Packages

  • Pip Installs Python (pip)
    • Standard Python package manager
    • Downloads packages and their dependencies from PyPI
    • Formerly, easy_install was the main installer
    • pip install ${PACKAGE_NAME}
  • Alternatively, use your distribution's installer
    • Debian-based: sudo apt install python3-${PACKAGE_NAME}
    • emerge (Gentoo), pacman (Arch), pamac (Manjaro), yum, ...

To avoid collisions between the distribution's package manager and PyPI, always use a virtual environment when installing from PyPI

Questions
and feedback...

Further Reading

Kevlin Henney (edt.) 97 Things Every Programmer Should Know O'Reilly (2010)
Mark Pilgrim Dive Into Python 3 (2nd edition) Apress (October 23, 2009)
Python Software Foundation Python Documentation http://docs.python.org/3/