Mini Project: Library Management System
Objectives
By the end of this chapter, you should be able to:
- Design several classes that collaborate with each other, not just one class in isolation
- Combine inheritance, custom exceptions,
abc, and@propertyin a single working program
💡 Why this matters: The Deck of Cards exercise used two classes. A real program usually needs more than that, with classes that reference and act on each other. A library, its books, and its members are a natural next step up in complexity.
The Project
Build a Library Management System: a program that tracks a collection of items (books and, as a bonus, other media types), the members who can borrow them, and the borrowing and returning process itself, entirely in memory for now (file persistence comes in a later module).
Core Requirements
LibraryItem (abstract base class)
- An
ABCwithtitleandis_checked_out(startingFalse) - An
@abstractmethodcalleditem_type()that subclasses must implement, returning a string like"Book"
Book (subclass of LibraryItem)
- Adds
authorandisbn - Implements
item_type()to return"Book" - A
@propertycalleddisplay_titlethat returns something like"The Hobbit by J.R.R. Tolkien"combiningtitleandauthor
Member
- Has a
nameand a list of currently borrowed items borrow(item): adds the item to the member’s list and marks it checked outreturn_item(item): removes the item from the member’s list and marks it available again
Library
- Holds a collection of
LibraryItems and a collection ofMembers add_item(item)/add_member(member)checkout(member, item): has the member borrow the item, but raises a customItemUnavailableErrorif it’s already checked outreturn_item(member, item): has the member return the itemfind_by_title(title): returns the first matching item, orNone
Custom Exception
ItemUnavailableError(Exception): raised bycheckout()when an item is already checked out
Suggested Structure
from abc import ABC, abstractmethod
class ItemUnavailableError(Exception):
pass
class LibraryItem(ABC):
def __init__(self, title):
self.title = title
self.is_checked_out = False
@abstractmethod
def item_type(self):
pass
class Book(LibraryItem):
def __init__(self, title, author, isbn):
super().__init__(title)
self.author = author
self.isbn = isbn
def item_type(self):
return "Book"
@property
def display_title(self):
return f"{self.title} by {self.author}"
class Member:
def __init__(self, name):
self.name = name
self.borrowed_items = []
def borrow(self, item):
item.is_checked_out = True
self.borrowed_items.append(item)
def return_item(self, item):
item.is_checked_out = False
self.borrowed_items.remove(item)
class Library:
def __init__(self):
self.items = []
self.members = []
def add_item(self, item):
self.items.append(item)
def add_member(self, member):
self.members.append(member)
def find_by_title(self, title):
for item in self.items:
if item.title == title:
return item
return None
def checkout(self, member, item):
if item.is_checked_out:
raise ItemUnavailableError(f"{item.title} is already checked out")
member.borrow(item)
def return_item(self, member, item):
member.return_item(item)
Fill in the pieces yourself rather than copying this directly: deciding which class is responsible for what is the real exercise here.
Bonus Extensions
- Add a second
LibraryItemsubclass, likeDVDorMagazine, with its ownitem_type(), and confirm yourLibraryhandles a mix of both without changes to its own code (that’s polymorphism, paying off). - Give
Membera limit (say, three items at once) and raise a customBorrowingLimitExceededErrorif they try to check out a fourth. - Add
Library.overdue_items(), using Module 8’sdatetimeif you want to track due dates.
Try It
- Build
LibraryItem,Book,Member, andLibrary, then add a few books and members, and check some books out. - Try checking out an already-checked-out book, and confirm
ItemUnavailableErroris raised. Then catch it and print a friendly message instead of letting the program crash. - Confirm
LibraryItem()on its own raisesTypeError, since it’s abstract. - Attempt at least one bonus extension.
Recap
You’ve now designed a small system of collaborating classes (an abstract base class, a concrete subclass, and two more classes that reference and act on both of them), using nearly everything from this module at once. That’s a meaningfully different skill from writing one class in isolation, and it’s the shape most real object-oriented code actually takes.
Next module: reading from and writing to files.