Chapter 23 of 24 · Python for Beginners — Complete Free Tutorial
Standard Library Essentials
In the Python for Beginners series we have mostly reached for third-party packages, but before you install anything, look around: Python ships with a standard library of over 200 modules — batteries included. In this chapter of the 24-chapter series we will tour the pieces you will actually use day to day: collections and itertools for data shaping, pathlib and datetime for files and time, json and re for data interchange and patterns, then argparse and logging for real command-line tools, and finally dataclasses plus functools.lru_cache for tidy classes and free speed.
collections and itertools
The collections module gives you smarter containers: Counter tallies items, defaultdict creates a default the first time you read a missing key, and namedtuple makes a lightweight class with named fields. The itertools module adds looping machinery that is fast because it is written in C. Together they often replace a page of manual loops.
from collections import Counter, defaultdict, namedtuple
import itertools
counts = Counter("abracadabra")
print(counts.most_common(2))
groups = defaultdict(list)
for word in ["bee", "owl", "bat"]:
groups[word[0]].append(word)
print(dict(groups))
Point = namedtuple("Point", "x y")
print(Point(3, 4), Point(3, 4).x)
pairs = itertools.product("ab", "12")
print(list(pairs))
print(list(itertools.islice(itertools.count(10), 3)))
by_category = {}
for item, category in [("book", "fun"), ("pen", "work"), ("mug", "fun")]:
by_category.setdefault(category, []).append(item)
print(by_category)
Expected output:
[('a', 5), ('b', 2)]
{'b': ['bee', 'bat'], 'o': ['owl']}
Point(x=3, y=4) 3
[('a', '1'), ('a', '2'), ('b', '1'), ('b', '2')]
[10, 11, 12]
{'fun': ['book', 'mug'], 'work': ['pen']}
pathlib and datetime
Stop concatenating path strings with os.path.join. pathlib.Path makes paths objects, so / means join and methods like exists() and read_text() read naturally. For time, the datetime module gives you datetime for a moment, timedelta for a span, and formatting with strftime.
from pathlib import Path
from datetime import datetime, timedelta
scratch = Path("/tmp") / "demo.txt"
scratch.write_text("hello pathlib")
print(scratch.name, scratch.suffix, scratch.exists(), scratch.read_text())
launch = datetime(2026, 1, 15, 9, 30)
print(launch.strftime("%Y-%m-%d %H:%M"))
print(launch + timedelta(days=7))
print(launch.strftime("%B %d, %Y"))
Expected output:
demo.txt .txt True hello pathlib
2026-01-15 09:30
2026-01-22 09:30:00
January 15, 2026
json and re
Almost every program talks to something else, and json is the lingua franca: json.dumps turns a dict into text, json.loads turns it back. When you need to pull structure out of unstructured text, reach for re. Regex looks frightening, but a few building blocks — \d+ for digits, [A-Z] for a capital, + and * to repeat — cover most jobs. Note the r"" raw strings: they keep backslashes literal.
import json
import re
payload = {"name": "Ada", "langs": ["Python", "Fortran"], "active": True}
text = json.dumps(payload, indent=2)
print(text.splitlines()[0])
back = json.loads(text)
print(back["langs"], back["active"])
msg = "Order A-1024 shipped on 2026-01-15"
order = re.search(r"[A-Z]-\d+", msg).group()
date = re.search(r"\d{4}-\d{2}-\d{2}", msg).group()
print(order, date)
print(re.sub(r"\d+", "#", "qty 12, price 5"))
Expected output:
{
['Python', 'Fortran'] True
A-1024 2026-01-15
qty #, price #
argparse and logging: a real CLI
Two more modules complete the toolbox. argparse declares your command-line flags and writes the help text for you, and logging replaces every print("DEBUG:", x) with levels, timestamps, and handlers you can turn up in production. Together they turn a script into a tool.
import argparse
import logging
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")
log = logging.getLogger(__name__)
parser = argparse.ArgumentParser(description="Greet someone.")
parser.add_argument("name", help="who to greet")
parser.add_argument("-n", "--times", type=int, default=1, help="how often")
args = parser.parse_args()
log.info("greeting %s x%s", args.name, args.times)
for _ in range(args.times):
print(f"Hello, {args.name}!")
Running it with python3 greet.py Ada -n 2:
Expected output:
INFO greeting Ada x2
Hello, Ada!
Hello, Ada!
dataclasses and functools
If you have written a class whose only job is to hold data, dataclasses.dataclass writes the constructor, the representation, and __eq__ for you. And functools.lru_cache memoizes a function, storing results so repeated calls with the same arguments are nearly free — the classic example is recursive Fibonacci, which drops from an exponential crawl to an instant answer.
from dataclasses import dataclass
from functools import lru_cache
@dataclass
class Student:
name: str
grades: list
@property
def average(self):
return sum(self.grades) / len(self.grades)
print(Student(name="Ada", grades=[90, 85]))
@lru_cache(maxsize=None)
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
print(fib(30))
Expected output:
Student(name='Ada', grades=[90, 85])
832040
Common mistakes
- Reinventing the container — hand-rolled tallying and grouping loops when
Counteranddefaultdictalready do it, in C. Reach for the library first. - String paths everywhere —
os.path.joinconcatenation breaks the moment paths get joined twice or read on Windows. Usepathlib.Pathobjects. - Swallowing JSON errors — catching every
ValueErroraroundjson.loadshides the real problem. Letjson.JSONDecodeErrorsay what was wrong. - Regex without raw strings — writing
"\d"instead ofr"\d"turns backslashes into accidental escapes. Prefix patterns withr. - print() as a logging strategy — prints cannot be filtered by level or sent to a file. Start with the
loggingmodule on day one.
Practice checklist
- Rebuild one of your older loops using
Counter,defaultdict, oritertools. - Convert a script that uses
os.pathtopathlibend to end. - Read a config file with
jsonand model it as adataclass. - Give a script a proper CLI with
argparse, including--helptext. - Add
loggingwith two levels (INFO and DEBUG) and switch between them with a flag.
What's next?
You have met the highlights of the standard library — the rest rewards browsing the index at docs.python.org. In the final chapter of this 24-chapter series we pull everything together: Python Best Practices and Where to Go Next turns these lessons into habits and points you at what to learn after this course.
Frequently Asked Questions
Python is a general-purpose language first released in 1991, designed around one idea: code is read far more often than it is written. English keywords and indentation instead of braces make it readable, the standard library covers files, JSON, dates and testing, and pip adds thousands more packages. The same language handles scripts, web APIs, data work and machine learning.
None at all. Python is widely considered the friendliest first language: no declaration keywords, no braces, no build step, and error messages that name the problem. This series begins at your first print() call. If you already read JavaScript or TypeScript you will adjust quickly, but no previous language is assumed anywhere in these 24 chapters.
Python 3 only, any release from 3.12 onwards. Python 2 reached end of life in 2021 and its print "hi" statements and xrange() no longer run. One historical quirk remains: installers ship the interpreter as python3 on macOS and Linux, while Windows usually offers python or the py launcher, and every command in this series works with that one-word swap.
More than you might expect. Everyday automation such as renaming files, cleaning spreadsheets or scraping pages; web backends with Django, Flask or FastAPI; data analysis and machine learning with pandas, NumPy and PyTorch; DevOps tooling, desktop apps and games. Python is also the classic glue language, calling C, Rust or Fortran libraries from short readable scripts.
Pure Python can run 10 to 100 times slower than C, yet that rarely blocks you: heavy numeric work lives in C extensions such as NumPy, which Python coordinates. Dynamic typing does push errors to runtime, surfacing as a TypeError on the line that finally uses the bad value. Type hints checked by mypy move those mistakes back into your editor.
Only the interpreter. Install CPython from python.org, version 3.12 or newer, confirm it with python3 --version, and open a plain text editor; VS Code with the Python extension is the recommendation here. The early chapters use no third-party packages, so there is nothing else to set up until virtual environments and pip arrive.
A dict is a real runtime mapping: keys may be any hashable value rather than only strings, insertion order has been guaranteed since Python 3.7, and a missing key raises KeyError unless you ask with get(). JavaScript objects coerce every key to a string and carry a prototype chain. A TypeScript interface is erased at compile time and describes shape only; TypedDict is Python's checked equivalent.
Reach for a list when items will be added, removed or reordered: it is ordered, mutable and rich with methods. Pick a tuple for fixed records and function returns, because it is immutable and hashable, so it can be a dictionary key. Choose a set for unique values and fast membership checks; it has no order and de-duplicates on creation.
Yes, gradually. Annotate public function signatures first; the interpreter ignores hints at runtime, so a checker such as mypy is what gives them teeth. Add tests the same way: pytest collects any test_*.py file and plain assert statements, and a suite that finishes in a tenth of a second is one you will run after every change. Both habits cost little now and much more later.
JavaScript and TypeScript keep the browser, so anything rendered to the user stays theirs. Python takes the server: Django for full-featured applications, Flask for small ones, FastAPI for typed async APIs where your annotations become automatic documentation. Your TypeScript habits transfer directly, and asyncio mirrors the event loop you already know, just with different keywords.