0%

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 . It is to make the shape exist.

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 before you are distracted by its body, and it means every later lesson is filling something in rather than inventing structure.

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 . A caller who has an item’s id does not need to be holding the item. That keeps the library in charge of its own catalog rather than trusting whatever object it is handed.

overdue_loans(on_day) takes a day, rather than reading a clock. Chapter 6 made this : a that asks the outside world for the time is much harder to test than one that is told. Every date in this project is a plain integer day number, for the same reason.

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 names. The rest of the chapter builds on them.