Python Series track

Project 10 · Practice Projects

Project 10 — Library Manager

Manage a book library with borrow/return tracking, member accounts, overdue detection, and full-text search.

Practice Projects1 min readFree lesson

Difficulty: Lv ★★★ | Prerequisite Knowledge: Intermediate Weeks 6-8 (File I/O, OOP)

Goal

Book registration, borrowing, and return CLI (OOP applied)

Requirements

  • Classes: Book, Member, Library
  • Commands: book add, book list, member add, borrow <isbn> <member_id>, return <isbn>, search <keyword>
  • JSON persistent storage

Challenge Task

  • Borrowing Period/Overdue days (datetime)
  • Borrowing history by member

Assignment

Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

solution.pyPython
"""Library Management — OOP + JSON"""
import json
from pathlib import Path
from dataclasses import dataclass, asdict, field
from typing import Optional

DB = Path("library.json")

@dataclass
class Book:
    title: str
    author: str
    isbn: str
    is_borrowed: bool = False
    borrower: Optional[str] = None

@dataclass
class Member:
    member_id: str
    name: str
    borrowed: list[str] = field(default_factory=list)

class Library:
    def __init__(self):
        self.books: list[Book] = []
        self.members: list[Member] = []
        self.load()

    def load(self):
        if not DB.exists(): return
        data = json.loads(DB.read_text(encoding="utf-8"))
        self.books = [Book(**b) for b in data.get("books", [])]
        self.members = [Member(**m) for m in data.get("members", [])]

    def save(self):
        DB.write_text(json.dumps({
            "books": [asdict(b) for b in self.books],
            "members": [asdict(m) for m in self.members],
        }, ensure_ascii=False, indent=2), encoding="utf-8")

    def add_book(self, title, author, isbn):
        self.books.append(Book(title, author, isbn))
        self.save()

    def add_member(self, mid, name):
        self.members.append(Member(mid, name))
        self.save()

    def borrow(self, isbn, member_id):
        book = next((b for b in self.books if b.isbn == isbn and not b.is_borrowed), None)
        member = next((m for m in self.members if m.member_id == member_id), None)
        if not book: raise ValueError(f"Loan not allowed: {isbn}")
        if not member: raise ValueError(f"No member: {member_id}")
        book.is_borrowed = True
        book.borrower = member_id
        member.borrowed.append(isbn)
        self.save()

    def return_book(self, isbn):
        book = next((b for b in self.books if b.isbn == isbn and b.is_borrowed), None)
        if not book: raise ValueError(f"Return not allowed: {isbn}")
        member = next((m for m in self.members if m.member_id == book.borrower), None)
        if member: member.borrowed.remove(isbn)
        book.is_borrowed = False
        book.borrower = None
        self.save()

    def search(self, keyword):
        return [b for b in self.books if keyword.lower() in b.title.lower() or keyword.lower() in b.author.lower()]

def main():
    lib = Library()
    while True:
        raw = input("\n> ").strip()
        if not raw: continue
        if raw == "quit": break
        try:
            parts = raw.split()
            cmd = parts[0]
            if cmd == "book" and parts[1] == "add":
                t = input("Title:"); a = input("Author:"); i = input("ISBN: ")
                lib.add_book(t, a, i); print("Registered")
            elif cmd == "book" and parts[1] == "list":
                for b in lib.books:
                    status = f"On loan ({b.borrower})" if b.is_borrowed else "In possession"
                    print(f"  [{status}] {b.title} / {b.author} / {b.isbn}")
            elif cmd == "member" and parts[1] == "add":
                m = input("ID: "); n = input("Name:")
                lib.add_member(m, n); print("Registered")
            elif cmd == "borrow":
                lib.borrow(parts[1], parts[2]); print("Loan completed")
            elif cmd == "return":
                lib.return_book(parts[1]); print("Return completed")
            elif cmd == "search":
                results = lib.search(parts[1])
                for b in results:
                    print(f"  {b.title} / {b.author}")
        except (IndexError, ValueError) as e:
            print(f"Error: {e}")

if __name__ == "__main__":
    main()

Learn this lesson with a mentor

Work through the Python Series track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now