Standard Library Tour

This section documents the current Python release line as published at the official Python documentation, which is the reference these pages are written and verified against. No specific patch version is pinned.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

Python ships "batteries included": the modules on this page cover filesystem access, structured data, text patterns, math, and command-line tooling without installing anything. The tutorial’s Brief Tour of the Standard Library and Brief Tour, Part II introduce many of these modules in context; the full, authoritative catalogue of every module is the Python Standard Library reference.

Filesystem and OS

os and sys

os exposes the operating system’s process and filesystem interface; sys exposes interpreter state, including the command-line arguments and the process exit code:

import os
import sys

print(os.getcwd())                      # current working directory
print(os.environ.get("HOME", ""))       # environment variable, with a default

if len(sys.argv) < 2:
    print("usage: script.py <name>", file=sys.stderr)
    sys.exit(1)                          # nonzero exit code signals failure to the calling shell

name = sys.argv[1]                       # sys.argv[0] is the script name itself
print(f"hello, {name}")
sys.exit(0)                              # 0 signals success

Full references: the os module and the sys module.

pathlib

pathlib.Path is the modern, object-oriented alternative to building paths from os.path strings. Opening, reading, writing, and joining paths with Path — plus the enter/exit protocol behind with — are covered in depth on Files and Context Managers; this page only adds the piece that belongs alongside glob below, directory-tree pattern matching:

from pathlib import Path

for csv_path in Path("data").glob("*.csv"):        # non-recursive, like glob.glob()
    print(csv_path)

for csv_path in Path("data").rglob("*.csv"):        # recursive, like glob.glob(..., recursive=True)
    print(csv_path)

Reference: the pathlib module.

glob

glob matches filesystem paths against shell-style wildcards (*, ?, […​]) and predates pathlib:

import glob

for path in glob.glob("data/*.csv"):
    print(path)

for path in glob.glob("data/**/*.csv", recursive=True):   # ** descends into subdirectories
    print(path)

Reference: the glob module.

shutil

shutil provides the higher-level file operations os does not: copying, moving, and recursively deleting whole trees:

import shutil

shutil.copy("notes.txt", "backup/notes.txt")   # copy one file (metadata included with copy2)
shutil.copytree("data", "data_backup")          # recursively copy a directory tree
shutil.rmtree("data_backup")                    # recursively delete a directory tree

Reference: the shutil module.

Data: Dates, JSON, Patterns, and Collections

datetime

from datetime import datetime, timedelta

now = datetime.now()
tomorrow = now + timedelta(days=1)
print(now.strftime("%Y-%m-%d"))              # formatted string, e.g. '2026-09-05'

parsed = datetime.strptime("2026-01-15", "%Y-%m-%d")   # parse a string back into a datetime
print(parsed.year, parsed.month, parsed.day)

Reference: the datetime module.

json

json.dumps() serializes Python objects (dicts, lists, strings, numbers, booleans, None) to a JSON string; json.loads() parses one back:

import json

data = {"name": "Ada", "languages": ["python", "ocaml"]}
text = json.dumps(data, indent=2)   # dict -> JSON string
print(text)

restored = json.loads(text)         # JSON string -> dict
assert restored == data

Reference: the json module.

re

re implements Perl-style regular expressions: search() finds the first match anywhere in the string, findall() returns every match, and sub() replaces matches. Quantifiers such as {3} (exactly three repeats) or \{2,4\} (two to four) and groups written with (…​) are the essentials for most patterns:

import re

text = "Call 555-1234 or 555-5678"
pattern = r"\d{3}-\d{4}"

print(re.findall(pattern, text))            # ['555-1234', '555-5678']

match = re.search(r"(\d{3})-(\d{4})", text)
if match:
    print(match.group(1), match.group(2))   # '555' '1234'

print(re.sub(pattern, "REDACTED", text))    # 'Call REDACTED or REDACTED'

Reference: the re module.

collections

Beyond the built-in list/dict/set/tuple covered on Collections, the collections module adds specialized container types. Counter tallies items, defaultdict supplies a default value instead of raising KeyError on a missing key, and namedtuple gives tuple elements names:

from collections import Counter, defaultdict, namedtuple

words = ["a", "b", "a", "c", "b", "a"]

counts = Counter(words)
print(counts)                     # Counter({'a': 3, 'b': 2, 'c': 1})
print(counts.most_common(1))      # [('a', 3)]

groups = defaultdict(list)
for word in words:
    groups[word[0]].append(word)  # no KeyError the first time a key is seen
print(dict(groups))

Point = namedtuple("Point", ["x", "y"])
p = Point(1, 2)
print(p.x, p.y)                   # 1 2 -- plus regular tuple behavior: p[0], unpacking, etc.

itertools

itertools builds lazy iterators for combinatorics and infinite/composite sequences — see Iterators, Generators, and Comprehensions for the iterator protocol these are built on:

import itertools

for a, b in itertools.product([1, 2], ["x", "y"]):
    print(a, b)                                            # (1,x) (1,y) (2,x) (2,y)

print(list(itertools.chain([1, 2], [3, 4])))               # [1, 2, 3, 4]
print(list(itertools.islice(itertools.count(10), 3)))      # [10, 11, 12] -- lazy infinite counter, sliced

Reference: the itertools module.

functools

reduce() folds an iterable down to a single value, lru_cache memoizes a function’s results, and partial() freezes some of a function’s arguments ahead of time:

from functools import reduce, lru_cache, partial

total = reduce(lambda acc, x: acc + x, [1, 2, 3, 4], 0)   # 10 (sum() is clearer for this specific case)

@lru_cache(maxsize=None)
def fib(n):
    return n if n < 2 else fib(n - 1) + fib(n - 2)

print(fib(30))                      # cached: repeat calls with the same n cost O(1) after the first

add_ten = partial(lambda x, y: x + y, 10)   # first argument frozen to 10
print(add_ten(5))                   # 15

Reference: the functools module.

Math and Random

math

import math

print(math.sqrt(2))                      # 1.4142135623730951
print(math.floor(3.7), math.ceil(3.2))   # 3 4
print(math.gcd(12, 18))                  # 6
print(math.pi)                           # 3.141592653589793

Reference: the math module.

random

import random

random.seed(42)                         # fixes the sequence -- useful for reproducible tests
print(random.randint(1, 6))             # random int in [1, 6], both ends inclusive
print(random.choice(["a", "b", "c"]))   # one random element

sample = list(range(10))
random.shuffle(sample)                  # shuffles in place, returns None
print(random.sample(range(100), 5))     # 5 unique values, no repeats

Reference: the random module.

statistics

import statistics

data = [2, 4, 4, 4, 5, 5, 7, 9]
print(statistics.mean(data))      # 5
print(statistics.median(data))    # 4.5
print(statistics.stdev(data))     # sample standard deviation

Reference: the statistics module.

CLI and Observability

argparse

argparse replaces hand-parsing sys.argv: declare positional and optional arguments once, and it builds the parsing, type conversion, defaults, and --help text for free:

import argparse

parser = argparse.ArgumentParser(description="Greet someone")
parser.add_argument("name")                            # positional, required
parser.add_argument("--shout", action="store_true")     # optional boolean flag
parser.add_argument("--times", type=int, default=1)      # optional value, converted to int

args = parser.parse_args()          # reads sys.argv by default

greeting = f"hello, {args.name}"
if args.shout:
    greeting = greeting.upper()
for _ in range(args.times):
    print(greeting)

A script built this way is typically installed and run as a console-script entry point inside its own virtual environment — see Virtual Environments and Packaging for packaging a CLI tool so it can be installed and invoked by name.

Reference: the argparse module.

logging

logging is the leveled, configurable alternative to scattering and later deleting print() calls. Levels run DEBUG < INFO < WARNING < ERROR < CRITICAL; only messages at or above the configured level are emitted:

import logging

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)

logger.debug("won't show at INFO level")
logger.info("starting job")
logger.warning("disk space low")
try:
    1 / 0
except ZeroDivisionError:
    logger.error("division failed", exc_info=True)   # exc_info=True attaches the traceback

Because the level is set in one place (basicConfig, or a config file), a whole codebase’s verbosity can be turned up or down without touching a single call site — something print()-debugging cannot offer.

Reference: the logging module.

See Also