Intermediate
Mini Project: Building a Console-Based Application
📂 Phase 6: Mini Projects, Coding Challenges, Interview Preparation (Days 26–30) · Python ProgrammingThis lesson ties together everything from Phases 1 through 4 — functions, OOP, file handling, and exception handling — into a single, complete, working project: a console-based Contact Book. Building one full project from scratch is far more valuable for interview readiness than studying any individual topic in isolation.
Project Overview
The Contact Book will let a user add, view, search, update, and delete contacts, with all data persisted to a JSON file so the contacts survive between program runs.
Step 1: The Contact Class
class Contact:
def __init__(self, name, phone, email):
self.name = name
self.phone = phone
self.email = email
def to_dict(self):
return {"name": self.name, "phone": self.phone, "email": self.email}
def __str__(self):
return f"{self.name} | {self.phone} | {self.email}"
Step 2: The ContactBook Class — Managing Storage
import json
import os
class ContactBook:
def __init__(self, filename="contacts.json"):
self.filename = filename
self.contacts = self.load_contacts()
def load_contacts(self):
if os.path.exists(self.filename):
with open(self.filename, "r") as file:
return json.load(file)
return []
def save_contacts(self):
with open(self.filename, "w") as file:
json.dump(self.contacts, file, indent=2)
def add_contact(self, contact):
self.contacts.append(contact.to_dict())
self.save_contacts()
print(f"Added {contact.name} successfully!")
Step 3: Search, Update, and Delete Logic
def search_contact(self, name):
results = [c for c in self.contacts if name.lower() in c["name"].lower()]
return results
def delete_contact(self, name):
original_count = len(self.contacts)
self.contacts = [c for c in self.contacts if c["name"].lower() != name.lower()]
if len(self.contacts) < original_count:
self.save_contacts()
print(f"Deleted {name}")
else:
print(f"No contact found named {name}")
Step 4: Adding Error Handling
def add_contact_safe(self, name, phone, email):
try:
if not name or not phone:
raise ValueError("Name and phone are required fields")
contact = Contact(name, phone, email)
self.add_contact(contact)
except ValueError as e:
print(f"Could not add contact: {e}")
Step 5: The Command-Line Menu Loop
def main():
book = ContactBook()
while True:
print("
1. Add Contact 2. Search 3. Delete 4. View All 5. Exit")
choice = input("Choose an option: ")
if choice == "1":
name = input("Name: ")
phone = input("Phone: ")
email = input("Email: ")
book.add_contact_safe(name, phone, email)
elif choice == "2":
name = input("Search for: ")
for contact in book.search_contact(name):
print(contact)
elif choice == "3":
name = input("Name to delete: ")
book.delete_contact(name)
elif choice == "4":
for contact in book.contacts:
print(contact)
elif choice == "5":
print("Goodbye!")
break
else:
print("Invalid option, try again")
if __name__ == "__main__":
main()
What This Project Demonstrates
| Concept | Where It Is Used |
|---|---|
| OOP (classes, encapsulation) | Contact and ContactBook classes |
| File handling | Persisting contacts to contacts.json |
| Exception handling | add_contact_safe validating input |
| List comprehensions | search_contact and delete_contact filtering logic |
| Functions and control flow | The main() menu loop |
The if __name__ == "__main__": Pattern
This line ensures main() only runs when the file is executed directly — not when it is imported as a module into another file. It is one of the most universal conventions in Python scripts and projects.
Interview tip: When asked to "build a small project," interviewers are usually evaluating structure and decision-making far more than raw cleverness — separating data (the Contact class) from logic (ContactBook) from the user interface (main()) shows a level of organization that a single giant function never demonstrates.