The Standard Library You Will Actually Use
A tour of what ships with Python and saves you writing it: pathlib, datetime, json, collections, itertools, random and argparse, each shown solving a real task.
A tour of what ships with Python and saves you writing it: pathlib, datetime, json, collections, itertools, random and argparse, each shown solving a real task.
Somewhere in your photo script there is a function that works out how many days ago a date was. It took you forty minutes, it has a subtle bug around month boundaries, and it is eleven lines long. The correct version is one line, ships with Python, and has been tested by millions of people.
Python's phrase for this is "batteries included": a large collection of modules that arrive with the interpreter and need no installation. By the end of this lesson you will know the dozen worth recognising by name, which is enough to stop you rebuilding them, and enough to make the documentation useful when you need the rest.
datetime handles anything to do with when something happened.
from datetime import datetime, date, timedelta
now = datetime.now()
today = date.today()
print(now.year, now.month, now.day)
print(today.isoformat()) # 2026-07-29Arithmetic uses timedelta, which represents a duration:
week_ago = today - timedelta(days=7)
deadline = now + timedelta(hours=48)
age = today - date(2020, 3, 15)
print(age.days) # a whole number of daysSubtracting two dates gives a timedelta; adding one to a date
gives a date. That is the entire model, and it removes every
month-boundary bug from the code you would have written.
Reading and writing text:
taken = datetime.strptime("2026-07-29 14:30", "%Y-%m-%d %H:%M")
print(taken.strftime("%d %B %Y")) # 29 July 2026strptime parses; strftime formats. The direction is
in the letter, which is the only way anyone remembers.
You met pathlib in the files lesson. Two companions come up
almost as often.
os reaches the operating system, and its most common use is
environment variables — which is where configuration and
secrets belong:
import os
api_key = os.environ.get("PHOTO_API_KEY")
home = os.environ["HOME"]shutil does the file operations pathlib leaves out — copying
whole trees, moving, deleting recursively:
import shutil
shutil.copy("dawn.jpg", "backup/dawn.jpg")
shutil.copytree("photos", "backup/photos")
shutil.move("dawn.jpg", "processed/")
shutil.rmtree("temporary") # deletes a whole treetempfile gives you scratch space that cleans itself up:
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as directory:
scratch = Path(directory) / "resized.jpg"
... # directory is deleted afterjson and csv you have met. Three more worth knowing:
sqlite3 deserves more attention than it gets. It is a complete
SQL database that lives in one file and needs nothing installed
— for a program that outgrows JSON but does not want a server,
it is often exactly right.
hashlib answers "are these two files the same" without
comparing them byte by byte:
collections holds specialised versions of the containers you
already know. Three of them earn their place immediately.
Tallying, with ranking for free.
The d[k] = d.get(k, 0) + 1 loop from the dictionaries
lesson, done for you — plus most_common(3).
Grouping, with no first-time case.
Reading a missing key creates the default instead of raising.
Right for building groups up, and wrong for lookups — where
you want the KeyError.
Fast at both ends, and boundable.
A list is slow at the front, because inserting there shifts
everything. maxlen also gives you "the last hundred" with
nothing to clean up.
Counter does the tallying you wrote by hand in the
dictionaries lesson, and adds ranking for free.
defaultdict removes the "first time" special case when
grouping:
Reading a missing key creates the default rather than raising,
which is exactly right for building up groups — and exactly
wrong for lookups, where you want the KeyError.
deque is a list that is fast at both ends. A normal list is
slow at the front, because inserting there shifts everything:
itertools covers the loop shapes that are fiddly to write
correctly.
And functools, whose most valuable member is a one-line cache:
Called again with the same argument, it returns the stored answer instead of doing the work. That decorator has probably saved more time per character than anything else in the standard library.
argparse turns a script into a proper tool:
You get --help for free, type conversion, and a clear error
when someone passes nonsense.
Bad — reading arguments by
position from sys.argv.
Good — declaring what the program accepts.
The first version crashes with IndexError: list index out of range when someone forgets an argument — a message that names
neither the program nor the missing thing. It has no help text,
so the only way to learn the argument order is to read the
source, and swapping two arguments silently processes the wrong
folder. argparse gives all of that back for three lines.
Enough to recognise, so you search rather than reinvent:
Two pairs on that list look interchangeable and are not.
secrets, for anything guessable.
random is predictable by design — given enough output,
somebody can work out what comes next.
Tokens, passwords and reset links go through secrets.
random is for shuffling and sampling.
logging, for anything unattended.
A print cannot be filtered by importance, redirected to a file, or switched off without editing code.
Keep print for things a person is watching happen.
You now know roughly where to look before writing something yourself. That habit compounds: standard-library code is installed everywhere, tested by everyone, and adds nothing to your dependencies, so choosing it is almost always right.
Next is Your First Real Python Program, the closing lesson of this course. It puts everything together — a command-line tool that reads real files, handles real errors, is split across modules, and has a test. Nothing new is introduced; the point is seeing the pieces work as one thing.
Before you move on, find a helper function you have written and
search the standard library for it. Look especially at
itertools, functools and collections. Finding that
something you built already existed is mildly deflating and
extremely useful — it recalibrates where you look first.
re regular expressions - pattern matching in text
random random numbers, shuffling, sampling
math sqrt, ceil, floor, pi, isclose
statistics mean, median, stdev
decimal exact decimal arithmetic - money
logging proper logging, instead of print
unittest testing, built in
subprocess run other programs
urllib fetch a URL without installing anything
zipfile read and write zip archives
uuid unique identifiers
textwrap wrap and indent text
secrets cryptographically safe random valuesTIME
datetime.now(timezone.utc) always store UTC
date.today()
timedelta(days=7) durations; date - date -> timedelta
strptime(text, fmt) parse
strftime(fmt) format
FILES AND SYSTEM
pathlib.Path paths, reading, globbing
os.environ.get("KEY") config and secrets
shutil copy, move, copytree, rmtree
tempfile.TemporaryDirectory() scratch space that cleans up
DATA
json nested data
csv tabular, DictReader
sqlite3 a real database in one file
hashlib.sha256(...).hexdigest() checksums
pickle NEVER on untrusted input
COLLECTIONS
Counter(items).most_common(3)
defaultdict(list) grouping without a first-time case
deque(maxlen=100) fast at both ends, bounded
LOOPS AND FUNCTIONS
itertools chain islice product groupby
functools @lru_cache(maxsize=256)
COMMAND LINE
argparse --help, types, real errors
not sys.argv[1]
WORTH KNOWING BY NAME
re random math statistics decimal logging unittest
subprocess urllib zipfile uuid textwrap secrets
secrets, not random, for anything guessable
logging, not print, for anything unattendedimport json # nested data, the web's common language
import csv # tabular data with a header row
import sqlite3 # a real database in a single file, no server
import pickle # any Python object -> bytes
import hashlib # checksums and digestsimport hashlib
digest = hashlib.sha256(Path("dawn.jpg").read_bytes()).hexdigest()from collections import Counter, defaultdict, deque
Counter(tags).most_common(3) # the three commonest, sortedby_city = defaultdict(list)
for photo in photos:
by_city[photo.city].append(photo) # no need to create the listrecent = deque(maxlen=100) # keeps only the last 100
recent.append(photo) # oldest falls off the frontfrom itertools import groupby, chain, islice, product
chain(list_a, list_b) # walk several as one
islice(photos, 10) # the first ten, lazily
product(sizes, formats) # every combinationfrom functools import lru_cache
@lru_cache(maxsize=256)
def expensive_lookup(photo_id):
...import argparse
parser = argparse.ArgumentParser(description="Process photos.")
parser.add_argument("folder", help="folder to process")
parser.add_argument("--dry-run", action="store_true",
help="report without writing")
parser.add_argument("--limit", type=int, default=100)
args = parser.parse_args()
print(args.folder, args.dry_run, args.limit)import sys
folder = sys.argv[1]
limit = int(sys.argv[2])parser.add_argument("folder")
parser.add_argument("--limit", type=int, default=100)
args = parser.parse_args()