Modules & the Standard Library

The Course
•
January 9, 2026
•
6 min read

Python ships with a large standard library — the “batteries included” its users have bragged about for thirty years. Knowing what is in the box saves you from writing it again.

Four ways to import

Import styles Python
import math                      # the whole module
print(math.sqrt(16), math.pi)

from math import sqrt, pi        # specific names
print(sqrt(25), pi)

import statistics as stats       # with a shorter alias
print(stats.mean([1, 2, 3, 4]))

from math import *               # everything - do not do this
print(cos(0))

Prefer the first two. import math keeps the origin of math.sqrt visible; from math import * dumps unknown names into your namespace and quietly shadows your own.

math and random

Numbers Python
import math
import random

print(math.sqrt(144), math.floor(3.7), math.ceil(3.2))
print(math.factorial(5), round(math.log(100, 10)))

random.seed(7)          # same seed, same sequence - handy for tests
print(random.randint(1, 6))
print(random.random())
print(random.choice(["rock", "paper", "scissors"]))

deck = list(range(1, 11))
random.shuffle(deck)
print(deck)
print(random.sample(deck, 3))

datetime

Dates and times Python
from datetime import date, datetime, timedelta

today = date(2026, 1, 9)
print(today, today.year, today.strftime("%A, %d %B %Y"))

launch = date(2026, 3, 1)
gap = launch - today
print(f"{gap.days} days until launch")

print(today + timedelta(days=90))

parsed = datetime.strptime("2026-01-09 14:30", "%Y-%m-%d %H:%M")
print(parsed, parsed.hour)

strftime formats a date into text; strptime parses text into a date. The codes are the same both ways: %Y four-digit year, %m month, %d day, %H hour, %M minute, %A weekday name, %B month name.

json

JSON is how programs exchange structured data. The mapping to Python is almost one to one: objects become dictionaries, arrays become lists.

Text in, data out Python
import json

data = {
    "name": "Ada",
    "languages": ["Python", "Analytical Engine"],
    "active": True,
    "score": None,
}

text = json.dumps(data, indent=2)     # Python -> JSON text
print(text)

restored = json.loads(text)           # JSON text -> Python
print(type(restored), restored["languages"][0])
print(restored["active"] is True, restored["score"] is None)

Note the translation: Python’s True becomes true, None becomes null. json.dump() and json.load() (no “s”) do the same to and from a file object.

collections

Three tools here save real work.

Counter, defaultdict, namedtuple Python
from collections import Counter, defaultdict, namedtuple

words = "the quick brown fox the lazy dog the end".split()

counts = Counter(words)
print(counts.most_common(2))
print(counts["the"], counts["missing"])    # missing keys are 0, not an error

groups = defaultdict(list)                 # a missing key auto-creates []
for word in words:
    groups[len(word)].append(word)
print(dict(groups))

Point = namedtuple("Point", ["x", "y"])    # a tuple with named fields
p = Point(3, 4)
print(p, p.x, p.y)

Counter is the word-count exercise from Day 3 in a single call.

pathlib

The modern way to handle paths — no string concatenation, no separators to get wrong.

Paths as objects Python
from pathlib import Path

folder = Path("data")
folder.mkdir(exist_ok=True)

note = folder / "note.txt"        # / joins paths on any OS
note.write_text("Written through pathlib\n")

print(note)
print(note.name, note.suffix, note.stem)
print(note.exists())
print(note.read_text())

for item in folder.iterdir():
    print("  found:", item)

Writing your own module

Any .py file is a module. The next playground holds two files — click the tabs above the editor to switch between them.

Two files, one program Python
from geometry import circle_area, rectangle_area print(f"Circle r=2: {circle_area(2):.2f}") print(f"Rectangle 3x4: {rectangle_area(3, 4)}") """Small geometry helpers.""" from math import pi def circle_area(radius): """Return the area of a circle.""" return pi * radius ** 2 def rectangle_area(width, height): """Return the area of a rectangle.""" return width * height

Imports look in the same folder first, so from geometry import ... finds geometry.py sitting next to main.py. That is all a module is.

if __name__ == '__main__'

Code at the top level of a module runs on import. Guarding it with if __name__ == "__main__": means it runs only when the file is executed directly — so a module can be both an importable library and a runnable script.

Before you write your own

Predict. Four modules’ worth of lines. The date one crosses a month.

What will this print? Python
from collections import Counter
from datetime import date, timedelta
import json

meals = Counter(["pasta", "rice", "pasta", "soup", "pasta"])
print(meals.most_common(1))
print(meals["pizza"])

start = date(2026, 1, 30)
print(start + timedelta(days=3))

print(json.dumps({"done": True, "note": None}))
Check your prediction
[('pasta', 3)]
0
2026-02-02
{"done": true, "note": null}

A Counter answers 0 for something it never saw, rather than raising. timedelta handles month ends for you. And JSON spells Python’s True and None as true and null.

Fix. A to-do list saved as JSON should come back as a list you can loop over. Instead this prints one character per line.

Text, not a list Python
import json

tasks = ["buy milk", "call Sam"]
text = json.dumps(tasks)

for task in text:
    print(task)
Check your fix
import json

tasks = ["buy milk", "call Sam"]
text = json.dumps(tasks)

for task in json.loads(text):
    print(task)
buy milk
call Sam

json.dumps returns a string — that’s what makes it saveable — and looping over a string gives you its characters. json.loads turns the text back into a list.

Exercise

A dice-rolling report

Roll two six-sided dice 1,000 times and report how often each total from 2 to 12 came up, as a percentage to one decimal place. Seed the generator with 42 so your run is reproducible.

You should get eleven lines, one for each total from 2 to 12, with 7 the most common — in the long run it comes up one time in six, so expect roughly 15 to 18%. Your exact percentages depend on the order you call random in, so they may not match the solution’s to the digit.

import random
from collections import Counter

random.seed(42)

# Your code here
Hint 1: the idea

Two parts: simulate, then report. One roll of two dice is random.randint(1, 6) + random.randint(1, 6). Doing that 1,000 times and counting how often each total appears is exactly what Counter is for. The report is then a loop over the totals 2 to 12.

Hint 2: a first step

Make one roll and print it. Then collect 1,000 of them in a list, and pass the list to Counter — print that, and you’ll see every total with its count. A count out of 1,000 becomes a percentage when you divide by 10.

Show one solution
import random
from collections import Counter

random.seed(42)

rolls = Counter(
    random.randint(1, 6) + random.randint(1, 6)
    for _ in range(1000)
)

for total in range(2, 13):
    count = rolls[total]
    bar = "#" * (count // 10)
    print(f"{total:2}: {count / 10:5.1f}%  {bar}")

A generator expression feeding Counter does the whole simulation in one statement, and rolls[total] returns 0 for any total that never came up.

What you learned

  • import module and from module import name are the imports to use.
  • math, random, datetime, json, collections and pathlib cover an enormous amount of everyday work.
  • JSON maps onto Python dictionaries and lists almost exactly.
  • Counter counts, defaultdict removes missing-key checks, namedtuple names tuple fields.
  • Any .py file next to yours is importable as a module.
Last updated on January 9, 2026

Was this article helpful?

Your response is saved on this device.