Chapter 8

Modules and the Standard Library

Every function you've written so far lived in the same file you were working in. Python also ships with a large collection of ready-made tools you didn't have to write, called the standard library. This chapter is how to reach for them, plus one idiom you'll see at the top of nearly every real Python script.

import

A module is a file of Python code, .py or built in, whose names you can bring into your own script with import:

import math

print(math.sqrt(16))
Output
4.0

import math makes the whole math module available under the name math; you reach its contents with a dot, math.sqrt. from ... import ... brings in one specific name directly, so you don't need the dot each time:

from math import sqrt

print(sqrt(16))
Output
4.0

Either style works; this book mostly uses import module_name and the dot, because it's clearer at a glance which module a name like sqrt came from.

A short tour

Three modules from the standard library, each with one clear job for this book's tracker.

math, for rounding a total up or down:

import math

total = 57.35
print(math.floor(total))
print(math.ceil(total))
Output
57
58

math.floor() rounds down, math.ceil() rounds up, both always toward the nearest whole number in that direction.

statistics, for a quick average:

import statistics

amounts = [4.50, 12.00, 32.10, 8.75]
print(statistics.mean(amounts))
Output
14.3375

statistics.mean() does the same job as the average() function you may have written in Chapter 7's practice, built in and ready to use.

random, for picking one entry to spot-check:

import random

expenses = [
    {"amount": 4.50, "category": "coffee", "note": ""},
    {"amount": 12.00, "category": "transit", "note": ""},
    {"amount": 32.10, "category": "groceries", "note": ""},
    {"amount": 8.75, "category": "coffee", "note": ""},
]

random.seed(7)
chosen = random.choice(expenses)
print(chosen)
Output
{'amount': 32.1, 'category': 'groceries', 'note': ''}

random.choice() picks one item from a list at random. random.seed(7) above makes that "random" choice repeatable, which is the only reason this example can be tested at all: with the same seed, random.choice() always makes the same pick. You won't normally seed it in a real script; it's shown here so the book's own claim, that every example was run and this is really what it printed, still holds for a module whose whole point is unpredictability.

datetime, for working with a date:

import datetime

started = datetime.date(2026, 9, 11)
print(started)
print(started.strftime("%B %d, %Y"))
Output
2026-09-11
September 11, 2026

datetime.date(2026, 9, 11) builds a specific date, year first, matching how you'd read it out loud least ambiguously. .strftime() formats it as text; %B is the full month name, %d the day, %Y the four-digit year. You'll meet datetime.date.today() in other people's code for "the date right now," left out of this book's own tested examples on purpose: a result that depends on today's date is exactly the kind of thing that can't be checked against a fixed expected answer, which every example in this book is.

sys.argv, a preview

input() isn't the only way a script can receive information from whoever runs it. sys.argv holds whatever was typed after the script's name on the command line, as a list of strings:

import sys

print(sys.argv)
Terminal
$ python show_args.py coffee 4.50
['show_args.py', 'coffee', '4.50']

sys.argv[0] is always the script's own name; everything after it is whatever the person typed. This is how a script can be run non-interactively, python track.py coffee 4.50, instead of pausing for input() each time, which matters the moment you want to run a script from another program or a scheduled task rather than by hand. Turning that list of strings into a proper set of named options, with help text and error messages of its own, is a job for the argparse module, which is its own short subject and not one this book covers.

Reaching for a module you haven't met

Not every module gets a mention here; the standard library has dozens. The habit that matters more than memorizing any particular one: when you need to do something that feels like it should be common, search "python" plus what you're trying to do before writing it from scratch. A surprising amount of everyday programming is already solved in a module you just haven't met yet.

name and the main-guard

Every module, including the file you're running directly, has a __name__. Run a file directly and its __name__ is "__main__":

print(__name__)
Output
__main__

That's a plain fact about the file you're currently in, not something special about this book: any script you run directly, python track.py, has __name__ equal to "__main__" for the whole time it runs. The useful part is what happens when a file is imported instead of run directly. In that case, its __name__ becomes the module's own name, not "__main__".

That difference is exactly what if __name__ == "__main__": checks for. It's the single most-asked "why is Python code written this way" question there is, and it's worth understanding, not just copying. Picture two files:

# tools.py
def add_expense(expenses, amount, category, note=""):
    expenses.append({"amount": amount, "category": category, "note": note})
    return expenses

if __name__ == "__main__":
    print("Run tools.py directly to see this.")
# track.py
import tools

expenses = []
tools.add_expense(expenses, 4.50, "coffee")
print(expenses)

Run python tools.py directly and its __name__ is "__main__", so the guarded line runs and you see the message. Run python track.py, which imports tools.py rather than running it directly, and tools.py's __name__ is "tools", so the guarded line never runs, only add_expense() gets used. The guard lets one file be both a reusable module and a runnable script, without the "runnable script" part firing every time someone else imports it. From Chapter 11 onward, this book's own script uses exactly this pattern.

Practice

Try each of these before you read the solution under it.

  1. Use math to print the floor and the ceiling of 19.20.
  2. Given amounts = [4.50, 12.00, 32.10, 8.75, 45.00], print the mean with statistics.mean().
  3. Explain, in a comment above a bare print(__name__), what it would print if this exact line lived in a file called helpers.py that got imported by another script, versus run directly. Then show what it actually prints when run directly, right here.
  4. Build datetime.date(2026, 1, 15) and print it formatted as "January 15, 2026" using .strftime().

Solutions

1.

import math

print(math.floor(19.20))
print(math.ceil(19.20))
Output
19
20

2.

import statistics

amounts = [4.50, 12.00, 32.10, 8.75, 45.00]
print(statistics.mean(amounts))
Output
20.47

3. Imported, it would print "helpers"; run directly, it prints "__main__", which is what this fence, run directly as part of the book's own build, actually shows.

# imported by another script: would print "helpers"
# run directly: prints "__main__"
print(__name__)
Output
__main__

4.

import datetime

d = datetime.date(2026, 1, 15)
print(d.strftime("%B %d, %Y"))
Output
January 15, 2026

Where this leaves you

You can import a module and use a few useful ones from the standard library, and you understand what __name__ is and why if __name__ == "__main__": exists, not just what it looks like. Chapter 9 puts real data on disk: reading and writing a text file.