I'm continuing my Python learning journey, and this time I built a Library Management System using Object-Oriented Programming (OOP).
This project was more than just writing code that works. It helped me understand how different objects can interact with each other and how to structure a program so that each class has a clear responsibility.
What I Built
The system manages:
- Books
- Library members
- Borrowing and returning books
- Available and borrowed books
- Searching for books and members
- Duplicate ISBN and member ID validation
- Error handling for invalid operations
The project was built around three main classes:
Library
├── Books
│ ├── Book
│ ├── Book
│ └── Book
│
└── Members
├── Member
└── Member
The Book Class
The Book class is responsible for managing the state of an individual book.
Each book has:
- An author
- A title
- An ISBN
- A borrowing status
I used a private-by-convention attribute called _is_borrowed to keep track of whether a book was borrowed.
self._is_borrowed = False
I then used a property to allow other parts of the program to check the status:
@property
def is_borrowed(self):
return self._is_borrowed
The book also has methods for borrowing and returning:
def borrow(self):
if self._is_borrowed:
raise ValueError(f"The book '{self.title}' is already borrowed.")
self._is_borrowed = True
This taught me an important OOP concept: encapsulation.
Instead of allowing other objects to directly change the book's internal state, the Book class controls how that state changes.
The Member Class
The Member class keeps track of the books a particular member has borrowed.
One thing I found interesting was that the borrowed_books list contains actual Book objects:
self.borrowed_books.append(book)
So instead of storing something like:
["1984"]
the member stores a reference to the actual Book object.
This allowed the Member and Book objects to interact with each other.
For example:
member_1.borrow_book(book1)
The member checks the book's state, asks the book to borrow itself, and then adds that book to the member's list.
That interaction helped me understand OOP much better.
The Library Class
The Library class acts as the central manager.
It keeps track of:
self.books = []
self.members = []
It can:
- Add books
- Register members
- Find books by ISBN
- Find members by ID
- Display available books
- Display borrowed books
I also added validation to prevent duplicate books and members.
For example:
def add_book(self, book):
if self.find_book(book.isbn):
raise ValueError(
f"The book with ISBN {book.isbn} already exists in the library."
)
self.books.append(book)
One lesson I learned here was the importance of reusing existing methods.
Instead of writing another loop to check for a duplicate ISBN, I reused find_book().
Error Handling
The project also gave me more practice with try and except.
For example, if someone tries to borrow a book that is already borrowed:
try:
member_2.borrow_book(book1)
except ValueError as e:
print(e)
This allows the program to handle the problem without crashing.
I also learned the difference between:
return None
and:
raise ValueError(...)
Returning None can simply mean that a search didn't find anything.
Raising a ValueError, on the other hand, signals that an invalid operation was attempted.
That distinction was something I initially misunderstood, but working through the project helped clarify it.
One Mistake I Made
One of the mistakes I made was accidentally creating two separate Library objects:
library = Library()
register_members = Library()
I added the books to one library and the members to the other.
The code technically ran, but the objects weren't being managed by the same library.
That helped me understand something important:
Every time I call:
Library()
I'm creating a new object with its own independent data.
The correct approach was to use one library:
library = Library()
library.add_book(book1)
library.add_book(book2)
library.register_member(member_1)
library.register_member(member_2)
That was a simple mistake, but it taught me a valuable lesson about object instances.
What I Learned
This project helped me practice and understand:
- Classes and objects
- Constructors
- Instance attributes
- Private-by-convention attributes
- Properties
- Methods
__str__- Object-to-object interaction
- Lists containing objects
- List comprehensions
- Generator expressions
all()- Exception handling
ValueErrortry/except- Object state
- Encapsulation
But the biggest lesson wasn't a particular Python keyword.
It was learning to think about responsibilities.
The Book class manages book state.
The Member class manages what a member has borrowed.
The Library class manages the collection of books and members.
Each object has a job, and the objects work together.
What's Next?
I'm continuing my Python learning journey and moving deeper into Object-Oriented Programming.
My next project will involve building a text-based RPG battle system, where I'll be working with inheritance, method overriding, abstract classes, and different types of characters.
One project at a time, I'm trying to move from simply understanding Python syntax to actually understanding how to think and build with Python.
Top comments (0)