Lesson 12 of 12

Python Modules and Error Handling

As your programs grow, two questions come up. How do I reuse code instead of writing everything myself? And what should happen when something goes wrong, such as a missing file or text where a number was expected? Python modules answer the first question and exceptions the second. This final lesson of the track brings together everything from Python Functions and Python File Handling.

Importing Python modules

A module is simply a Python file whose functions and variables you can use in another file. Python ships with a large standard library of modules, ready to import without installing anything. There are three common forms of import:

FormHow you use it
import mathPrefix every name: math.sqrt(16)
from math import sqrt, piUse the names directly: sqrt(16)
import statistics as statsA shorter alias: stats.mean(data)

The first form is the clearest, because the reader can see where each name comes from. Avoid from module import *: it pulls in every name and can silently replace your own variables. Put all imports at the top of the file.

import math
import statistics as stats
from datetime import date

print(math.sqrt(144), math.ceil(4.2), math.floor(4.8))
print(f"Area of a 7 m radius garden: {math.pi * 7 ** 2:.1f} sq m")

rainfall_mm = [120, 85, 240, 310, 95]
print("Mean:", stats.mean(rainfall_mm), "Median:", stats.median(rainfall_mm))

republic_day = date(2026, 1, 26)
tamil_new_year = date(2026, 4, 14)
print(republic_day.strftime("%A, %d %B %Y"))
print((tamil_new_year - republic_day).days, "days apart")
Output
12.0 5 4
Area of a 7 m radius garden: 153.9 sq m
Mean: 170 Median: 120
Monday, 26 January 2026
78 days apart

Other Python modules worth knowing early are random (random numbers and choices), pathlib, csv and json (from the files lesson), collections and os. The library reference lists them all.

import random

random.seed(2026)
prizes = ["Saree", "Smartwatch", "Gift voucher", "Cookbook"]
print(random.choice(prizes))
print(random.randint(1, 100))

random.seed(2026)
print(random.choice(prizes))
Output
Saree
41
Saree

Random numbers are usually different on every run. Calling random.seed() with a fixed number makes the sequence repeat, which is useful for testing: after seeding again with 2026, choice() gives the same prize as before. The exact values can differ between Python versions, so do not rely on a seed for results that must stay identical for ever. For passwords or security tokens, use the secrets module instead of random.

Writing your own module

Any .py file is a module. Save the code below as shop_utils.py. In another file in the same folder, such as main.py, you can then write from shop_utils import add_gst and call add_gst(500).

GST_RATE = 18

def add_gst(amount, rate=GST_RATE):
    """Return the amount including GST."""
    return round(amount * (1 + rate / 100), 2)

def format_rupees(amount):
    return f"₹{amount:,.2f}"

if __name__ == "__main__":
    # Runs only when this file is run directly, not when it is imported
    print(format_rupees(add_gst(500)))
    print(format_rupees(add_gst(12000, rate=5)))
Output
₹590.00
₹12,600.00

The special variable __name__ is "__main__" when you run the file directly, and the module’s name when it is imported. The if __name__ == "__main__": block therefore runs the quick tests above only when you run shop_utils.py itself. When main.py imports the module, the functions are loaded and nothing is printed.

Installing packages with pip

Beyond the standard library, thousands of free packages are published on the Python Package Index (PyPI). For each project, first create a virtual environment, a private folder of packages, with python -m venv .venv (use py -m venv .venv on Windows). Activate it, then install packages with python -m pip install followed by the package name. This keeps every project’s packages separate.

Errors and exceptions

When Python meets a problem while running, it raises an exception and, if nothing handles it, stops and prints a traceback. Read a traceback from the bottom up: the last line names the exception type and message, such as ValueError: invalid literal for int() with base 10: 'abc', and the lines above show the file and line number where it happened.

ExceptionTypical cause
ValueErrorRight type, wrong value: int("abc")
TypeErrorWrong type: "5" + 5
KeyErrorMissing dictionary key
IndexErrorList index out of range
ZeroDivisionErrorDividing by zero
FileNotFoundErrorOpening a file that does not exist

Handling errors with try and except

Put code that might fail in a try block and the recovery code in an except block for a specific exception type. An optional else block runs only if no exception happened, and finally always runs, which suits clean-up work. You can also raise exceptions yourself with raise when a function receives a value it cannot accept.

def split_bill(total_text, people):
    if people < 1:
        raise ValueError("people must be at least 1")
    total = float(total_text)
    return total / people

for total_text, people in [("1800", 4), ("eighteen hundred", 4), ("1800", 0)]:
    try:
        share = split_bill(total_text, people)
    except ValueError as error:
        print(f"Could not split {total_text!r} between {people}: {error}")
    else:
        print(f"Each person pays ₹{share:.2f}")
    finally:
        print("-- checked --")
Output
Each person pays ₹450.00
-- checked --
Could not split 'eighteen hundred' between 4: could not convert string to float: 'eighteen hundred'
-- checked --
Could not split '1800' between 0: people must be at least 1
-- checked --

Follow the three test cases. The first works, so else prints the share. In the second, float() raises a ValueError. The third fails on purpose: our own raise stops an impossible split before the program divides by zero. except ValueError as error stores the exception in a variable so you can show its message, and !r in the f-string prints the value with its quotes, which makes empty or odd text easy to spot. finally runs every time.

Watch out: Do not write a bare except: or catch Exception just to make errors disappear. It hides real bugs, including typos in your own code. Catch only the exceptions you expect and know how to handle, and let the rest show their traceback.

Tip: You have finished the Python track. A good next step is a small project that combines the lessons, such as an expense tracker that saves to CSV or JSON. For ideas on what to learn next, compare this track with the JavaScript track.

Further reading

Key takeaways

  • A module is a .py file; bring it in with import module, from module import name or import module as alias.
  • The standard library (math, datetime, statistics, random, pathlib, csv, json…) is available without installing anything.
  • Use if __name__ == "__main__": for code that should run only when a file is run directly.
  • Install extra packages with pip inside a virtual environment, one per project.
  • Handle expected errors with try/except, use else and finally when needed, and raise for invalid input.

Quick quiz

Check what you learned – pick an answer.

1. After from math import sqrt, how do you call the function?

2. When does the else block of a try statement run?

3. Which exception does int("12a") raise?

Practice exercise

Write a function safe_average(values) that converts each item of a list of strings to a float, skips (and reports) items that are not numbers, and returns the average of the valid ones. Raise a ValueError if no valid numbers remain. Test it with ["42", "38.5", "n/a", "45"].

Show a hint

Loop over the values with a try/except ValueError around float(). Collect valid numbers in a list, then check whether the list is empty before dividing.

Show the solution
def safe_average(values):
    numbers = []
    for value in values:
        try:
            numbers.append(float(value))
        except ValueError:
            print(f"Skipping {value!r}")
    if not numbers:
        raise ValueError("no valid numbers")
    return sum(numbers) / len(numbers)

print(f"Average: {safe_average(['42', '38.5', 'n/a', '45']):.2f}")

try:
    safe_average(["-", "?"])
except ValueError as error:
    print("Error:", error)

Progress is saved in this browser only.