Lesson 14 — Organising Code

Modules

A Python module is any .py file. Organising code into modules makes it reusable, testable, and maintainable. The standard library provides an enormous amount of ready-to-use functionality.

PYTHON — Standard library
# Importing the whole module:
import math
import random
import os

print(math.pi)          # 3.141592653589793
print(math.sqrt(25))    # 5.0
print(math.floor(4.7))  # 4
print(random.randint(1, 100))
print(os.getcwd())      # current working directory
PYTHON — Selective imports
# Import specific names (from module import name):
from math import pi, sqrt, floor
from datetime import date, timedelta
from pathlib import Path

print(sqrt(16))           # 4.0 — no math. prefix
print(date.today())       # 2025-04-01
deadline = date.today() + timedelta(days=30)
print(f"Deadline: {deadline}")
print(Path.cwd())         # current directory as Path object

Creating Your Own Module

PYTHON — grading.py module
# grading.py — save as a separate file

VAT_RATE = 0.15

def grade_label(mark):
    """Return grade label for a numeric mark."""
    if mark >= 80: return "Distinction"
    if mark >= 70: return "Merit — Upper"
    if mark >= 60: return "Merit"
    if mark >= 50: return "Pass"
    return "Not yet competent"

def class_stats(marks):
    """Return dict with mean, min, max, pass_rate."""
    n = len(marks)
    if n == 0:
        return {}
    passed = sum(1 for m in marks if m >= 50)
    return {
        "mean":      sum(marks) / n,
        "min":       min(marks),
        "max":       max(marks),
        "pass_rate": passed / n
    }

# Guards against running when imported:
if __name__ == "__main__":
    print("Testing grading module...")
    print(grade_label(74))
    print(class_stats([65, 72, 80, 58, 91]))
PYTHON — main.py using the module
# main.py — in the same directory
from grading import grade_label, class_stats, VAT_RATE

marks = [65, 72, 80, 58, 91, 44, 77]

for mark in marks:
    print(f"{mark}%: {grade_label(mark)}")

stats = class_stats(marks)
print(f"Mean: {stats['mean']:.1f}%")
print(f"Pass rate: {stats['pass_rate']:.0%}")

The __name__ == "__main__" Guard

When Python runs a file directly, it sets __name__ to "__main__". When a file is imported as a module, __name__ is the module name. The guard prevents test code from running when the module is imported.

Practice Task

Your Turn

Create a stats.py module with: average(marks), median(marks), std_dev(marks), percentile(marks, p) returning the p-th percentile. Add the __name__ guard with tests. Import and use all four functions in a main script with a list of 10 marks.

Common Mistakes

  • Circular imports — A imports B, B imports A. Restructure your code.
  • Naming your file the same as a standard library module (e.g. math.py) — your file shadows the standard library.
  • from module import * pollutes the namespace — avoid it.
  • Forgetting __name__ == "__main__" guard — test code runs every time the module is imported.

Professional Tip

The standard library covers 80% of common needs — before installing a package, check if Python has built-in support. The docs at docs.python.org are comprehensive and well-written.

Mini Quiz

What does the if __name__ == '__main__' guard do?