Checkpoint: The Standard Library
Reviewed & published by Brayan K
A checkpoint is a hands-on review where you combine several standard-library modules — functools, enums, namedtuple, hashlib, and more — into one small working tool.
Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
You have learned a lot of powerful modules. This is where they stop being separate lessons and start being a toolkit you reach into to solve a real problem.
What You've Learned in This Section
- functools — lru_cache, partial, reduce, wraps
- enums — named constants with .name / .value
- namedtuple & NamedTuple — immutable records
- random — numbers, picks, shuffles, seeding
- subprocess — running external commands safely
- hashlib — digests and password KDFs
- unittest — TestCase, assertions, fixtures
- typing — generics, Protocol, TypedDict
1 Quick Recap of Your Toolkit
@lru_cache memoizes pure functions; partial pre-fills arguments; reduce folds a sequence; @wraps preserves a decorated function's name.
Group fixed choices as named members with .name and .value; iterate them; use IntEnum for ordered comparisons.
Immutable records with named fields; copy with _replace; the typing.NamedTuple class form adds defaults and methods.
sha256(...).hexdigest() fingerprints data; encode strings first; hashing is one-way, and passwords need a salted KDF.
2 🛠️ Build Challenge: An Order-Summary Tool
Build a tiny order-processing tool, one step at a time, using four modules together:
- Enum — define a Status enum with NEW, PAID, and SHIPPED.
- namedtuple — define an Order record with id, amount, and status fields.
- lru_cache — write a cached with_tax(amount) that adds 8% tax; repeated amounts should be cache hits.
- hashlib — fingerprint the final report string with sha256 so any change is detectable.
Compute the tax-inclusive total of the billable orders (status PAID or SHIPPED), then hash a short report of the result. Start from the runnable scaffold below:
# 🛠️ BUILD CHALLENGE: order-summary tool (starter — already runnable)
from enum import Enum
from collections import namedtuple
from functools import lru_cache
import hashlib
# Step 1: statuses as an enum
class Status(Enum):
NEW = "new"
PAID = "paid"
SHIPPED = "shipped"
# Step 2: an Order record
Order = namedtuple("Order", ["id", "amount", "status"])
# Step 3: cached tax computation (repeated amounts are free)
@lru_cache(maxsize=None)
def with_tax(amount):
return round(amount * 1.08, 2)
orders = [
Order(1, 100.0, Status.PAID),
Order(2, 50.0, Status.NEW),
Order(3, 100.0, Status.SHIPPED), # same amount as order 1 -> cache hit
]
# Keep only billable orders (PAID or SHIPPED)
billable = [o for o in orders if o.status in (Status.PAID, Status.SHIPPED)]
total = sum(with_tax(o.amount) for o in billable)
print("billable count:", len(billable)) # billable count: 2
print("total with tax:", total) # total with tax: 216.0
print("cache hits:", with_tax.cache_info().hits) # cache hits: 1
# Step 4: fingerprint the report so any change is detectable
report = f"count={len(billable)};total={total}"
digest = hashlib.sha256(report.encode("utf-8")).hexdigest()
print("digest:", digest[:16]) # digest: dc1af27a92780cbf
# ✅ Expected output:
# billable count: 2
# total with tax: 216.0
# cache hits: 1
# digest: dc1af27a92780cbffrom enum import Enum
from collections import namedtuple
from functools import lru_cache
import hashlib
import unittest, io
class Status(Enum):
NEW = "new"
PAID = "paid"
SHIPPED = "shipped"
Order = namedtuple("Order", ["id", "amount", "status"])
@lru_cache(maxsize=None)
def with_tax(amount):
return round(amount * 1.08, 2)
def summarize(orders):
billable = [o for o in orders if o.status in (Status.PAID, Status.SHIPPED)]
total = sum(with_tax(o.amount) for o in billable)
report = f"count={len(billable)};total={total}"
digest = hashlib.sha256(report.encode("utf-8")).hexdigest()
return len(billable), total, digest
orders = [
Order(1, 100.0, Status.PAID),
Order(2, 50.0, Status.NEW),
Order(3, 100.0, Status.SHIPPED),
]
count, total, digest = summarize(orders)
print(count, total, digest[:16]) # 2 216.0 dc1af27a92780cbf
# Verify the tool with a unittest
class TestSummary(unittest.TestCase):
def test_total(self):
c, t, _ = summarize(orders)
self.assertEqual(c, 2)
self.assertEqual(t, 216.0)
def test_excludes_new(self):
# An all-NEW order list is billable-count zero
only_new = [Order(9, 10.0, Status.NEW)]
c, t, _ = summarize(only_new)
self.assertEqual(c, 0)
self.assertEqual(t, 0)
suite = unittest.TestLoader().loadTestsFromTestCase(TestSummary)
result = unittest.TextTestRunner(stream=io.StringIO(), verbosity=0).run(suite)
print("tests passed:", result.wasSuccessful()) # tests passed: True3 🎯 Stretch Goal: Add Priorities
Extend the idea: model tasks with an IntEnum priority and a namedtuple, then sum a cached cost over the high-priority tasks and fingerprint the summary. This reuses the same four-module pattern on fresh data.
from enum import Enum
from collections import namedtuple
from functools import lru_cache
import hashlib
class Priority(Enum):
LOW = 1
HIGH = 2
Task = namedtuple("Task", ["name", "cost", "priority"])
@lru_cache(maxsize=None)
def doubled(cost):
return cost * 2
tasks = [
Task("deploy", 30, Priority.HIGH),
Task("docs", 10, Priority.LOW),
Task("hotfix", 30, Priority.HIGH), # same cost as deploy -> cache hit
]
high = [t for t in tasks if t.priority is Priority.HIGH]
total = sum(doubled(t.cost) for t in high)
print("high-priority tasks:", len(high)) # high-priority tasks: 2
print("total budget:", total) # total budget: 120
print("cache hits:", doubled.cache_info().hits) # cache hits: 1
summary = f"high={len(high)};budget={total}"
print("fingerprint:", hashlib.sha256(summary.encode("utf-8")).hexdigest()[:12])
# fingerprint: 87cf98aa6cd5
# ✅ Expected output:
# high-priority tasks: 2
# total budget: 120
# cache hits: 1
# fingerprint: 87cf98aa6cd5📝 Checkpoint Quiz
Answer each from memory, then expand to check yourself.
1. What does @lru_cache require of a function's arguments, and why?
They must be hashable (e.g. numbers, strings, tuples — not lists or dicts), because the cache stores results in a dictionary keyed by the arguments. Passing a list raises TypeError: unhashable type: 'list'.
2. Why is Color.RED == 1 False for a plain Enum, and how do you change that?
A plain Enum member is its own object, not its raw value — that is a safety feature. If you genuinely need members to compare equal to integers, subclass IntEnum instead, where Priority.MEDIUM == 2 is True.
3. How do you "change" a field on a namedtuple?
You cannot mutate it — it is immutable. You build a new copy with _replace: p = p._replace(x=10). The original is untouched, so remember to assign the result back.
4. Why must you call .encode() before hashing a string with hashlib?
Hash functions operate on bytes, not text. A str must be encoded (almost always UTF-8) first: hashlib.sha256("hi".encode("utf-8")). Forgetting raises "Unicode-objects must be encoded before hashing."
5. Why is passing a list of arguments safer than shell=True in subprocess?
With a list, each element is treated as one literal argument, so user input can never be reinterpreted as extra shell commands. shell=True hands a string to a shell to parse, which is the classic command-injection risk when any part of that string is untrusted.
6. Do type hints raise a TypeError at runtime when an argument's type is wrong?
No. Python ignores hints while running — double("ab") on a function annotated -> int just runs. Hints are checked by static tools like mypy and by your editor, not the interpreter.
📚 Keep Learning
Bookmark the modules you used in this checkpoint:
- The Python Standard Library — full index
- functools reference
- hashlib reference
Frequently Asked Questions
Checkpoint complete — the standard library is your toolkit now!
You combined an enum, a namedtuple, an lru_cache'd computation, and a sha256 fingerprint into one working tool — and verified it with a unittest. That is exactly how these modules are used together in real software.
Practice quiz
What does @lru_cache require of a function's arguments, and why?
- They must be integers, because the cache uses array indexes
- They must be immutable strings only
- They must be hashable, because results are stored in a dict keyed by the arguments
- They must be positional, not keyword
Answer: They must be hashable, because results are stored in a dict keyed by the arguments. lru_cache stores results in a dictionary keyed by the arguments, so they must be hashable. Passing a list raises TypeError: unhashable type: list.
For a plain Enum, why is Color.RED == 1 False, and how do you change it?
- A plain Enum member is its own object, not its raw value — subclass IntEnum to compare equal to ints
- It is a bug; use .value to compare
- Enums never support ==
- Use str(Color.RED) instead
Answer: A plain Enum member is its own object, not its raw value — subclass IntEnum to compare equal to ints. A plain Enum member is a distinct object, not its raw value — a safety feature. IntEnum members compare equal to their integer values.
How do you "change" a field on a namedtuple?
- Assign directly: p.x = 10
- Call p.set("x", 10)
- Convert to a list, edit, convert back
- Use p._replace(x=10), which returns a new copy
Answer: Use p._replace(x=10), which returns a new copy. Namedtuples are immutable. _replace returns a new copy with the changed field; the original is untouched, so assign the result back.
Why must you call .encode() before hashing a string with hashlib?
- To make the hash shorter
- Hash functions operate on bytes, not text, so the str must be encoded first
- Because sha256 is case-sensitive
- To add a salt automatically
Answer: Hash functions operate on bytes, not text, so the str must be encoded first. Hash functions work on bytes. A str must be encoded (almost always UTF-8) first, or Python raises "Unicode-objects must be encoded before hashing."
Why is passing a list of arguments safer than shell=True in subprocess?
- Each list element is a literal argument, so input cannot be reinterpreted as shell commands
- Lists run faster
- shell=True is deprecated
- Lists allow piping
Answer: Each list element is a literal argument, so input cannot be reinterpreted as shell commands. With a list, each element is one literal argument and cannot become extra shell commands. shell=True hands a string to a shell to parse — the classic injection risk.
Do type hints raise a TypeError at runtime when an argument has the wrong type?
- Yes, always
- Yes, but only for return types
- No — Python ignores hints at runtime; static tools like mypy check them
- Only when assertions are enabled
Answer: No — Python ignores hints at runtime; static tools like mypy check them. Python ignores annotations while running. Hints are validated by static checkers like mypy and your editor, not the interpreter.
What does functools.partial do?
- Splits a function into smaller functions
- Pre-fills some arguments, returning a new callable that takes the rest
- Caches the function results
- Runs the function partially then pauses
Answer: Pre-fills some arguments, returning a new callable that takes the rest. partial pre-binds arguments to a function and returns a new callable that only needs the remaining ones.
What does functools.reduce do over a sequence?
- Removes duplicate items
- Filters out falsy items
- Returns the shortest element
- Folds the sequence into a single value by applying a function cumulatively
Answer: Folds the sequence into a single value by applying a function cumulatively. reduce applies a two-argument function cumulatively across the items, folding them into one final value.
In the order-summary build, which orders are counted as billable?
- Only NEW orders
- Orders with status PAID or SHIPPED
- All orders regardless of status
- Only orders above a price threshold
Answer: Orders with status PAID or SHIPPED. The tool keeps orders whose status is PAID or SHIPPED, then sums their tax-inclusive amounts.
Why does the build challenge fingerprint the report string with sha256?
- To compress the report
- To encrypt the data so it cannot be read
- So any change to the result is detectable via a stable digest
- To sort the orders
Answer: So any change to the result is detectable via a stable digest. A sha256 digest is a fingerprint of the exact report text; if the result changes, the digest changes, making tampering or drift detectable.
Continue this course
- Previous: Advanced Type Hints (Generics, Protocol, TypedDict)
- Next: Dictionary Methods Deep Dive — get, setdefault, update, pop, items and dict comprehensions in depth
- Quick reference: Python cheat sheet
Frequently asked questions
What is this checkpoint testing?
This checkpoint pulls together everything from the standard-library section: functools (lru_cache, partial, reduce), enums, namedtuple and NamedTuple, the random module, subprocess, hashlib, unittest, and advanced type hints. Instead of one isolated topic, you build a small tool that combines several of them, which is exactly how these modules are used in real projects — together.
How should I approach the build challenge?
Work top-down and one step at a time. First define the enum of statuses, then the namedtuple record type, then the cached computation, and finally the hash of the result. Run the code after each step so you catch mistakes early. If you get stuck, the full solution is provided in a collapsible section — but try each step yourself first, because struggling a little is where the learning happens.
Why combine an enum, a namedtuple, a cache, and a hash in one tool?
Because that combination mirrors real software. Enums give your statuses safe, named values; namedtuples give you lightweight immutable records; lru_cache makes a repeated computation instant; and a sha256 of the output gives you a fingerprint to detect change or verify integrity. Seeing them work together shows how standard-library pieces snap into a clean, professional design.
What comes after this checkpoint?
The Final Project. Having proven you can wire several standard-library modules into one working tool, you will tackle a larger end-to-end build that brings together the whole course. This checkpoint is the last skill-consolidation step before that capstone.