31 — Records: a small address book
A record is one dict with several fields. An address book is a list of those records saved as JSON. This guide builds a menu-driven book that adds a contact, lists everyone, and searches by name — each change saved to a file.
Steps
One contact is a dict with a
nameand aphone. The whole book is a list of those dicts:
run it →mina = {"name": "Mina", "phone": "010-1234"} contacts = [mina] show f"{contacts[0]['name']}: {contacts[0]['phone']}"Run it; it prints
Mina: 010-1234.contacts[0]is the first dict and['name']picks the field out of it.A real book loads a saved file, or begins empty when the file does not exist yet. Guide 15 used
os.path.existsthe same way:
run it →import os use file latest if os.path.exists("address.json"): contacts = json_load("address.json") else: contacts = []json_loadreturns the whole list of dicts, exactly as it was saved.addgrows the list withappendand writes it back.json_saveaccepts a list, not just a dict:
run it →contacts.append({"name": name, "phone": phone}) json_save("address.json", contacts)listwalks the list with aforloop and prints both fields of each record:
run it →for contact in contacts: show f"{contact['name']}: {contact['phone']}"searchfilters the same loop.find in contact["name"]is true when the typed text appears anywhere in the name, soMinfindsMina:
run it →for contact in contacts: if find in contact["name"]: show f"{contact['name']}: {contact['phone']}"The whole menu in one file. Save
address.nme:
run it →# address.nme — a small address book in a JSON file. # Run: nme r address # Type add, list, search, or quit. import os use file latest # Load saved contacts, or start with an empty list. if os.path.exists("address.json"): contacts = json_load("address.json") else: contacts = [] while True: show "" show "Commands: add, list, search, quit" ask command, "? " if command == "add": ask name, "Name? " ask phone, "Phone? " contacts.append({"name": name, "phone": phone}) json_save("address.json", contacts) show f"Saved {name}: {phone}" elif command == "list": show f"{len(contacts)} contacts" for contact in contacts: show f"{contact['name']}: {contact['phone']}" elif command == "search": ask find, "Search? " for contact in contacts: if find in contact["name"]: show f"{contact['name']}: {contact['phone']}" elif command == "quit": show "Bye!" break else: show "Unknown command"Run it and feed the commands through a pipe:
printf 'add\nMina\n010-1234\nlist\nsearch\nMin\nquit\n' | nme r addressCommands: add, list, search, quit ? Name? Phone? Saved Mina: 010-1234 Commands: add, list, search, quit ? 1 contacts Mina: 010-1234 Commands: add, list, search, quit ? Search? Mina: 010-1234 Commands: add, list, search, quit ? Bye!addsaves the new contact toaddress.json; the nextnme r addressloads it back, so the book keeps its contacts between runs.while True:never ends on its own, soquitmustbreakout — the menu shape from guide 22.Korean writes the same menu with
파일 사용 최신,물어봐, andjson저장. The full Korean program is in the Korean guide; this snippet loads the saved book:
run it →파일 사용 최신 if os.path.exists("address.json"): 연락처 = json읽기("address.json") else: 연락처 = []
Try it yourself
Add an email field to every contact: ask for it in add, save it in the
dict, and print it in list. The saved JSON changes shape, and old files
without the field still load — a missing field simply prints nothing.
What you learned
- A record is a dict; a book is a list of dicts saved as JSON.
json_savewrites a list of dicts just like a single dict.while True:with aquitcommand andbreakmakes a menu that never ends on its own.find in contact["name"]searches for text inside a field.os.path.existslets the first run start with an empty list.