Challenge 1: Contact Book
Objectives
By the end of this chapter, you should be able to:
- Design a small program using more than one class working together
- Persist a program’s data to disk between runs with JSON
- Export structured data to CSV
💡 Why this matters: This is the first project in the course where nothing is handed to you piece by piece. You’ll decide how the classes fit together, not just fill in a single function. That’s the actual shape of most real programming work.
The Project
Build a Contact Book: a program that stores contacts (name, email, phone number), keeps them in memory as a collection of objects, and saves that collection to a file so it’s still there the next time the program runs.
Core Requirements
Contact
A class representing one person, with:
name,email, andphoneattributes, set in__init__- A
__str__method so printing a contact shows something readable, like"Jordan Reyes <jordan@example.com>" - A
to_dict()method that returns the contact as a plain dictionary. This is what makes saving it to JSON straightforward.
ContactBook
A class that manages a collection of Contact instances, with:
add_contact(contact): adds a newContactto the collectionfind_contact(name): returns the first contact whose name matches, orNoneif there isn’t oneremove_contact(name): removes a contact by nameall_contacts(): returns every contact currently stored
Persistence
save_to_file(filename): writes every contact in the book to a JSON file, using each contact’sto_dict()andjson.dump(..., indent=2)load_from_file(filename): reads a JSON file back into aContactBook, rebuilding aContactinstance for each entry. If the file doesn’t exist yet, start with an empty book instead of raising an error.
CSV Export
export_to_csv(filename): writes every contact out to a CSV file withname,email, andphonecolumns, usingcsv.DictWriter
Suggested Structure
You don’t have to follow this exactly, but it’s a reasonable starting skeleton:
import json
import csv
class Contact:
def __init__(self, name, email, phone):
self.name = name
self.email = email
self.phone = phone
def __str__(self):
return f"{self.name} <{self.email}>"
def to_dict(self):
return {"name": self.name, "email": self.email, "phone": self.phone}
class ContactBook:
def __init__(self):
self.contacts = []
def add_contact(self, contact):
self.contacts.append(contact)
def find_contact(self, name):
for contact in self.contacts:
if contact.name == name:
return contact
return None
def remove_contact(self, name):
self.contacts = [c for c in self.contacts if c.name != name]
def all_contacts(self):
return self.contacts
def save_to_file(self, filename):
with open(filename, "w") as f:
json.dump([c.to_dict() for c in self.contacts], f, indent=2)
def load_from_file(self, filename):
try:
with open(filename) as f:
data = json.load(f)
except FileNotFoundError:
data = []
self.contacts = [Contact(**entry) for entry in data]
def export_to_csv(self, filename):
with open(filename, "w") as f:
writer = csv.DictWriter(f, fieldnames=["name", "email", "phone"])
writer.writeheader()
for contact in self.contacts:
writer.writerow(contact.to_dict())
Fill in the pieces yourself rather than copying this directly. The value here is in designing the interactions between Contact and ContactBook, not in the exact syntax.
Example Session
Here’s what a short session looks like once Contact and ContactBook are working:
book = ContactBook()
book.add_contact(Contact("Priya", "priya@example.com", "555-0101"))
book.add_contact(Contact("Sam", "sam@example.com", "555-0102"))
print(book.find_contact("Priya"))
# Priya <priya@example.com>
book.save_to_file("contacts.json")
book.export_to_csv("contacts.csv")
contacts.json then contains:
[
{"name": "Priya", "email": "priya@example.com", "phone": "555-0101"},
{"name": "Sam", "email": "sam@example.com", "phone": "555-0102"}
]
And contacts.csv contains:
name,email,phone
Priya,priya@example.com,555-0101
Sam,sam@example.com,555-0102
Try It
- Build
ContactandContactBook, add a handful of contacts, and print each one using__str__. - Save your contact book to a JSON file, then write a second small script that loads it back and confirms the contacts are all there.
- Export your contact book to a CSV file and open it in a spreadsheet program to confirm it looks right.
- Test
find_contactandremove_contactagainst a name you know exists, and one you know doesn’t.
Recap
You’ve now designed and built a small multi-class program with real persistence: the same shape as most everyday scripts, just smaller. The next lesson builds on this same project with a set of optional extensions.
Next lesson: extend the contact book with custom exceptions, decorators, and generators.