Statement
Solution
Python Exercise 9.2: Library Management
Put your OOP skills to work on a real-world system. Build a mini library that tracks books, members, and borrowing — using multiple classes that interact with each other.
Define three classes:
- Book — stores
title,author, and anis_availableflag (defaultTrue). Has a__str__method. - Member — stores
nameand aborrowed_bookslist (default empty). Has aborrow(book)method that marks the book unavailable and appends it to the member's list, rejecting if the book is already borrowed. Has areturn_book(book)method that marks it available again and removes it from the list. - Library — stores a
bookslist. Hasadd_book(book),show_available()(prints all available books), andshow_all()(prints every book with its status).
Sample Interaction:
Input
Output
(None)
--- Available Books ---
"1984" by George Orwell
"Dune" by Frank Herbert
"Sapiens" by Yuval Noah Harari
Alice borrows "1984"...
Alice borrows "Dune"...
Bob tries to borrow "1984"...
Error: "1984" is not available.
--- Available Books ---
"Sapiens" by Yuval Noah Harari
Alice returns "1984"...
--- All Books ---
"1984" by George Orwell [Available]
"Dune" by Frank Herbert [Borrowed]
"Sapiens" by Yuval Noah Harari [Available]
Solution
Each class has a single, clear responsibility. Library manages the catalogue; Member manages borrowing state; Book tracks its own availability.
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
self.is_available = True
def __str__(self):
return f'"{self.title}" by {self.author}'
class Member:
def __init__(self, name):
self.name = name
self.borrowed_books = []
def borrow(self, book):
if not book.is_available:
print(f'Error: "{book.title}" is not available.')
return
book.is_available = False
self.borrowed_books.append(book)
print(f'{self.name} borrows "{book.title}"...')
def return_book(self, book):
if book in self.borrowed_books:
book.is_available = True
self.borrowed_books.remove(book)
print(f'{self.name} returns "{book.title}"...')
class Library:
def __init__(self):
self.books = []
def add_book(self, book):
self.books.append(book)
def show_available(self):
print("--- Available Books ---")
for book in self.books:
if book.is_available:
print(book)
def show_all(self):
print("--- All Books ---")
for book in self.books:
status = "Available" if book.is_available else "Borrowed"
print(f"{book} [{status}]")
# Demo
b1 = Book("1984", "George Orwell")
b2 = Book("Dune", "Frank Herbert")
b3 = Book("Sapiens", "Yuval Noah Harari")
lib = Library()
lib.add_book(b1)
lib.add_book(b2)
lib.add_book(b3)
alice = Member("Alice")
bob = Member("Bob")
lib.show_available()
print()
alice.borrow(b1)
alice.borrow(b2)
bob.borrow(b1) # should fail
print()
lib.show_available()
print()
alice.return_book(b1)
print()
lib.show_all()
Key Concepts:
- Composition —
Libraryholds a list ofBookobjects;Memberholds a list of borrowedBookobjects. Classes work together without inheriting from each other. - Mutating an object's attribute (
book.is_available = False) inside one class is immediately visible to all other classes holding a reference to the same object. - Guard clauses keep methods short and readable — check for error conditions first and return early.
list.remove(item)removes the first matching object from a list.- The
inoperator works on lists of objects, checking by identity/equality.
Test Console
Run code to see output...