Chapter 14 · practice
Final Capstone: A Community Lending Library
Choose the Problem and Read the Contract
Thirteen chapters have handed you the pieces. This one asks you to build something with all of them at once.
You are writing a community lending library: it registers members, catalogs things worth borrowing, lends them out, takes them back, says what is available, and finds what is overdue. No web, no database, no files, no dates beyond a plain day number. Everything interesting about it is design.
This chapter starts a fresh project. Your files persist from lesson to lesson within this chapter; edit the named file in the project tree. Run always executes main.py, which remains a placeholder until the final lesson. Submit checks the current milestone across the project. Reset restores the chapter seed and removes your work, while keeping completion checkmarks.
What a contract is
You are not being asked to invent the interface. It is given, and it is called a contract, because it is the promise the rest of the world may rely on.
register_member(member)
add_item(item)
checkout(item_id, member_id, checkout_day)
return_item(item_id)
available_items()
active_loans()
overdue_loans(on_day)
Seven operations on one class, Library. Everything else in the project exists to make these seven work.
This is how real work usually arrives. Someone else has decided what the thing must do, sometimes because other code already calls it, and your job is to build something that keeps those promises. The freedom you have is real, but it is underneath the contract: how you store items, what you name your helpers, whether Book and Game share a base class. None of that is promised to anyone, so none of it is fixed.
Start with the shape, not the behavior
The first move on a project this size is not to write the hardest
class Library:
def register_member(self, member):
raise NotImplementedError
NotImplementedError is Python’s way of saying this is meant to exist and does not work yet. It is not an apology; it is a placeholder that fails loudly. If something calls it by accident, you find out immediately rather than getting a silent None.
A file full of these is worth more than it looks. It gives you a checklist you can run, it forces you to decide each method’s
Why write raise NotImplementedError instead of leaving the method body as pass?
Reading a signature carefully
Two details in the contract are easy to skim past, and both are decisions someone made.
checkout(item_id, member_id, checkout_day) takes identifiers, not
overdue_loans(on_day) takes a day, rather than reading a clock. Chapter 6 made this
What to build now
Create Library in library.py, with all seven methods, each raising NotImplementedError.
Get the parameter names right. Later lessons, and the checks, use them.
Task
Write the public shape of Library in library.py.
Define one class, Library, with an __init__ that takes nothing but self, and these seven
register_member(self, member)add_item(self, item)checkout(self, item_id, member_id, checkout_day)return_item(self, item_id)available_items(self)active_loans(self)overdue_loans(self, on_day)
Every method body is raise NotImplementedError. Nothing works yet, and nothing should pretend to.
Use exactly these names, in this order, with these