Editor

Library Management System in Python

Intermediate · Useful tools · about 75 minutes to build · 150 lines

Keep track of books and members, issue a book with a due date and work out the fine when it comes back late.

The program

Press Run and answer its questions in the box that appears under the code.

library_management_system.py
"""A library management system: books, members, issue and return with due dates."""
from datetime import date, datetime, timedelta
LOAN_DAYS = 14
FINE_PER_DAY = 5
DATE_FORMAT = "%d-%m-%Y" # 05-10-2026
STARTING_BOOKS = [
("B101", "Wings of Fire", "A. P. J. Abdul Kalam"),
("B102", "Malgudi Days", "R. K. Narayan"),
("B103", "The Jungle Book", "Rudyard Kipling"),
("B104", "Pride and Prejudice", "Jane Austen"),
]
STARTING_MEMBERS = {"M1": "Ananya Gupta", "M2": "Rahul Verma"}
MENU = "1) Books 2) Members 3) Add book 4) Add member 5) Issue 6) Return 7) Quit"
class Book:
"""A book on the shelf, and who has it while it is out."""
def __init__(self, title, author):
self.title = title
self.author = author
self.borrower = None # Member ID while the book is out
self.due = None # Date it must come back
def issue(self, member_id, day):
"""Lend the book to a member for LOAN_DAYS days."""
self.borrower = member_id
self.due = day + timedelta(days=LOAN_DAYS)
def take_back(self, day):
"""Mark the book as returned and return how many days late it is."""
days_late = max(0, (day - self.due).days)
self.borrower, self.due = None, None
return days_late
# Asking for IDs and dates
def ask_id(prompt, table):
"""Ask for an ID that is in the table (any case); Enter returns ""."""
while True:
text = input(prompt).strip().upper()
if text == "" or text in table:
return text
print(" No such ID. Check the list, or press Enter to cancel.")
def ask_date(prompt):
"""Ask for a date like 05-10-2026; Enter means today."""
while True:
text = input(f"{prompt} (DD-MM-YYYY, Enter for today): ").strip()
if text == "":
return date.today()
try:
return datetime.strptime(text, DATE_FORMAT).date()
except ValueError:
print(" Please type the date like 05-10-2026.")
# Library jobs
def show_books(books):
"""Print every book with its status."""
print(f" {'ID':<6}{'Title':<21}{'Author':<22}Status")
for book_id, book in books.items():
status = "Available"
if book.borrower:
status = f"Out to {book.borrower}, due {book.due.strftime(DATE_FORMAT)}"
print(f" {book_id:<6}{book.title:<21}{book.author:<22}{status}")
def issue_book(books, members):
"""Ask for a book, a member and a date, then lend the book."""
book = books.get(ask_id("Book ID: ", books)) # None if cancelled
if book is None:
return
if book.borrower:
print(f" {book.title} is out until {book.due.strftime(DATE_FORMAT)}.")
return
member_id = ask_id("Member ID: ", members)
if not member_id:
return
book.issue(member_id, ask_date("Issue date"))
print(f" {book.title} issued to {members[member_id]}. "
f"Due back on {book.due.strftime(DATE_FORMAT)}.")
def return_book(books):
"""Take a book back and work out any late fine."""
book = books.get(ask_id("Book ID: ", books))
if book is None:
return
if book.borrower is None:
print(f" {book.title} is not out.")
return
days_late = book.take_back(ask_date("Return date"))
if days_late:
print(f" Returned {days_late} day(s) late. Fine: {days_late * FINE_PER_DAY}")
else:
print(" Returned on time. No fine.")
def main():
"""Set up the library and run the menu until the user quits."""
books = {} # Book ID -> Book
for book_id, title, author in STARTING_BOOKS:
books[book_id] = Book(title, author)
members = dict(STARTING_MEMBERS) # Member ID -> name (a copy we can change)
print("Library management system")
while True:
print()
print(MENU)
choice = input("Choose 1-7: ").strip()
if choice == "1":
show_books(books)
elif choice == "2":
for member_id, name in members.items():
count = sum(1 for book in books.values() if book.borrower == member_id)
print(f" {member_id:<5}{name:<18}{count} book(s) out")
elif choice == "3":
title, author = input("Title: ").strip(), input("Author: ").strip()
if title and author:
book_id = f"B{101 + len(books)}"
books[book_id] = Book(title, author)
print(f" Added book {book_id}.")
else:
print(" A book needs a title and an author.")
elif choice == "4":
name = input("Member name: ").strip()
if name:
member_id = f"M{len(members) + 1}"
members[member_id] = name
print(f" Added member {member_id}.")
else:
print(" A member needs a name.")
elif choice == "5":
issue_book(books, members)
elif choice == "6":
return_book(books)
elif choice == "7":
print("Goodbye.")
break
else:
print(" Please choose a number from 1 to 7.")
if __name__ == "__main__":
main()

Open it in the full editor to save it, share it and keep building.

Sample run

What the program printed, and what was typed (in yellow).

Library management system

1) Books  2) Members  3) Add book  4) Add member  5) Issue  6) Return  7) Quit
Choose 1-7: 1
  ID    Title                Author                Status
  B101  Wings of Fire        A. P. J. Abdul Kalam  Available
  B102  Malgudi Days         R. K. Narayan         Available
  B103  The Jungle Book      Rudyard Kipling       Available
  B104  Pride and Prejudice  Jane Austen           Available

1) Books  2) Members  3) Add book  4) Add member  5) Issue  6) Return  7) Quit
Choose 1-7: 5
Book ID: b102
Member ID: m1
Issue date (DD-MM-YYYY, Enter for today): 01-10-2026
  Malgudi Days issued to Ananya Gupta. Due back on 15-10-2026.

1) Books  2) Members  3) Add book  4) Add member  5) Issue  6) Return  7) Quit
Choose 1-7: 5
Book ID: B102
  Malgudi Days is out until 15-10-2026.

1) Books  2) Members  3) Add book  4) Add member  5) Issue  6) Return  7) Quit
Choose 1-7: 4
Member name: Sara Thomas
  Added member M3.

1) Books  2) Members  3) Add book  4) Add member  5) Issue  6) Return  7) Quit
Choose 1-7: 5
Book ID: B104
Member ID: M3
Issue date (DD-MM-YYYY, Enter for today): 3rd Oct
  Please type the date like 05-10-2026.
Issue date (DD-MM-YYYY, Enter for today): 03-10-2026
  Pride and Prejudice issued to Sara Thomas. Due back on 17-10-2026.

1) Books  2) Members  3) Add book  4) Add member  5) Issue  6) Return  7) Quit
Choose 1-7: 6
Book ID: B102
Return date (DD-MM-YYYY, Enter for today): 19-10-2026
  Returned 4 day(s) late. Fine: 20

1) Books  2) Members  3) Add book  4) Add member  5) Issue  6) Return  7) Quit
Choose 1-7: 1
  ID    Title                Author                Status
  B101  Wings of Fire        A. P. J. Abdul Kalam  Available
  B102  Malgudi Days         R. K. Narayan         Available
  B103  The Jungle Book      Rudyard Kipling       Available
  B104  Pride and Prejudice  Jane Austen           Out to M3, due 17-10-2026

1) Books  2) Members  3) Add book  4) Add member  5) Issue  6) Return  7) Quit
Choose 1-7: 7
Goodbye.

How it is built

1. The Book class

A book knows its title and author and, while it is out, who has it and when it is due. issue works out the due date with timedelta, and take_back returns how many days late the book is.

class Book:
    """A book on the shelf, and who has it while it is out."""

    def __init__(self, title, author):
        self.title = title
        self.author = author
        self.borrower = None  # Member ID while the book is out
        self.due = None       # Date it must come back

    def issue(self, member_id, day):
        """Lend the book to a member for LOAN_DAYS days."""
        self.borrower = member_id
        self.due = day + timedelta(days=LOAN_DAYS)

    def take_back(self, day):
        """Mark the book as returned and return how many days late it is."""
        days_late = max(0, (day - self.due).days)
        self.borrower, self.due = None, None
        return days_late

2. Dates in DD-MM-YYYY form

datetime.strptime reads the text with DATE_FORMAT and raises ValueError for dates like 31-02-2026. Pressing Enter uses today's date.

def ask_date(prompt):
    """Ask for a date like 05-10-2026; Enter means today."""
    while True:
        text = input(f"{prompt} (DD-MM-YYYY, Enter for today): ").strip()
        if text == "":
            return date.today()
        try:
            return datetime.strptime(text, DATE_FORMAT).date()
        except ValueError:
            print("  Please type the date like 05-10-2026.")

3. Listing the books

Each book shows Available, or which member has it and the due date, turned back into text with strftime.

def show_books(books):
    """Print every book with its status."""
    print(f"  {'ID':<6}{'Title':<21}{'Author':<22}Status")
    for book_id, book in books.items():
        status = "Available"
        if book.borrower:
            status = f"Out to {book.borrower}, due {book.due.strftime(DATE_FORMAT)}"
        print(f"  {book_id:<6}{book.title:<21}{book.author:<22}{status}")

4. Issuing a book

The function stops early when the user cancels or the book is already out. books.get() gives None for a cancelled ID instead of raising KeyError.

def issue_book(books, members):
    """Ask for a book, a member and a date, then lend the book."""
    book = books.get(ask_id("Book ID: ", books))  # None if cancelled
    if book is None:
        return
    if book.borrower:
        print(f"  {book.title} is out until {book.due.strftime(DATE_FORMAT)}.")
        return
    member_id = ask_id("Member ID: ", members)
    if not member_id:
        return
    book.issue(member_id, ask_date("Issue date"))
    print(f"  {book.title} issued to {members[member_id]}. "
          f"Due back on {book.due.strftime(DATE_FORMAT)}.")

5. Returning a book and the fine

Subtracting two dates gives a timedelta, and its .days is the number of days late. The fine is that number times FINE_PER_DAY.

def return_book(books):
    """Take a book back and work out any late fine."""
    book = books.get(ask_id("Book ID: ", books))
    if book is None:
        return
    if book.borrower is None:
        print(f"  {book.title} is not out.")
        return
    days_late = book.take_back(ask_date("Return date"))
    if days_late:
        print(f"  Returned {days_late} day(s) late. Fine: {days_late * FINE_PER_DAY}")
    else:
        print("  Returned on time. No fine.")

6. Setting up the library

Books live in a dictionary keyed by ID and members in another. dict(STARTING_MEMBERS) makes a copy, so adding a member never changes the constant.

def main():
    """Set up the library and run the menu until the user quits."""
    books = {}  # Book ID -> Book
    for book_id, title, author in STARTING_BOOKS:
        books[book_id] = Book(title, author)
    members = dict(STARTING_MEMBERS)  # Member ID -> name (a copy we can change)
    print("Library management system")
    while True:
        print()
        print(MENU)
        choice = input("Choose 1-7: ").strip()
        if choice == "1":
            show_books(books)
        elif choice == "2":
            for member_id, name in members.items():
                count = sum(1 for book in books.values() if book.borrower == member_id)
                print(f"  {member_id:<5}{name:<18}{count} book(s) out")
        elif choice == "3":
            title, author = input("Title: ").strip(), input("Author: ").strip()
            if title and author:
                book_id = f"B{101 + len(books)}"
                books[book_id] = Book(title, author)
                print(f"  Added book {book_id}.")
            else:
                print("  A book needs a title and an author.")
        elif choice == "4":
            name = input("Member name: ").strip()
            if name:
                member_id = f"M{len(members) + 1}"
                members[member_id] = name
                print(f"  Added member {member_id}.")
            else:
                print("  A member needs a name.")
        elif choice == "5":
            issue_book(books, members)
        elif choice == "6":
            return_book(books)
        elif choice == "7":
            print("Goodbye.")
            break
        else:
            print("  Please choose a number from 1 to 7.")

Make it bigger

  • Save books and members to CSV files so the library keeps its records between runs.
  • Limit each member to three books at a time.
  • Add a search by title or author.
  • List every overdue book on a chosen date with the fine owed so far.

What it practises