Skip to content

ይህ ትምህርት ገና ወደ አማርኛ አልተተረጎመም፤ ስለዚህ በእንግሊዝኛ ቀርቧል። የእንግሊዝኛውን ገጽ ክፈቱ

Project: Contact book

5 min readፕሮጀክት

Time to build something you use every day: a contact book. You type commands, and it adds, finds, deletes, and lists contacts, all kept in a dictionary. Here's the finished program in use:

Output
Command: add
Name: Dawit
Number: 0900 000 004
Saved Dawit.
Command: find
Name: Yonas
No contact called Yonas.
Command: list
1. Abel: 0900 000 001
2. Dawit: 0900 000 004
3. Hana: 0900 000 002
4. Selam: 0900 000 003
Command: quit
Goodbye!

It combines a dict (lesson 4.4), enumerate and sorted (4.6), a while loop (Module 3), and match (lesson 2.5). You'll build it in five milestones, running it after each one. The Input box for each milestone already holds commands that try every part. The numbers are made up.

Milestone 1: list the contacts

Start with three contacts already in a dict, and print them numbered, in alphabetical order. sorted() on a dict sorts its keys, the names; each name then looks up its number. Call the loop's counter position: in milestone 3, number will mean a phone number, and one name shouldn't mean two things.

መልመጃ

A numbered list

Print every contact, numbered from 1, sorted by name.

Output
1. Abel: 0900 000 001
2. Hana: 0900 000 002
3. Selam: 0900 000 003

Your code

contacts = {"Selam": "0900 000 003", "Abel": "0900 000 001", "Hana": "0900 000 002"}
# Print each contact, numbered and sorted by name

In the editor, press Escape then Tab to move on.

መፍትሄውን አሳይ

This is one way to solve it, not the only one. If yours prints the same thing, it works.

contacts = {"Selam": "0900 000 003", "Abel": "0900 000 001", "Hana": "0900 000 002"}
for position, name in enumerate(sorted(contacts), start=1):
    print(f"{position}. {name}: {contacts[name]}")

Milestone 2: the menu loop

Now ask for commands until the user types quit. A match inside while True picks what to do; case _ catches anything else. One detail to be sure of first:

ጥያቄ

A break inside a case runs. What does it leave?

መልመጃ

Commands in a loop

Ask Command: over and over. list prints the contacts as in milestone 1, quit prints Goodbye! and stops, and anything else prints Unknown command. Try add, find, delete, list, or quit.

Your code

contacts = {"Selam": "0900 000 003", "Abel": "0900 000 001", "Hana": "0900 000 002"}
for position, name in enumerate(sorted(contacts), start=1):
    print(f"{position}. {name}: {contacts[name]}")

In the editor, press Escape then Tab to move on.

One answer per line, given to input() in order.

መፍትሄውን አሳይ

This is one way to solve it, not the only one. If yours prints the same thing, it works.

contacts = {"Selam": "0900 000 003", "Abel": "0900 000 001", "Hana": "0900 000 002"}
while True:
    command = input("Command: ")
    match command:
        case "list":
            for position, name in enumerate(sorted(contacts), start=1):
                print(f"{position}. {name}: {contacts[name]}")
        case "quit":
            print("Goodbye!")
            break
        case _:
            print("Unknown command. Try add, find, delete, list, or quit.")

Milestone 3: add and find

add asks for a name and a number and saves them. What if the name is already there? Predict:

ውጤቱን ገምቱ

Decide before you look. Guessing wrong is how this sticks.

contacts = {"Hana": "0900 000 002"}
contacts["Hana"] = "0900 000 222"
print(len(contacts), contacts["Hana"])
Pick the output

Python prints

1 0900 000 222

A key appears once in a dict, so saving Hana again replaces her number instead of adding a second Hana. That's fine, but the user should be told: print Updated instead of Saved.

find asks for a name and prints the number. A missing name must get a friendly message, never a KeyError: check with in first (lesson 4.4).

መልመጃ

Add and find

Add two cases. add asks Name: and Number: , saves the contact, and prints Saved Dawit. for a new name or Updated Hana. for one already there. find asks Name: and prints Hana: 0900 000 222, or No contact called Yonas.

Your code

contacts = {"Selam": "0900 000 003", "Abel": "0900 000 001", "Hana": "0900 000 002"}
while True:
    command = input("Command: ")
    match command:
        case "list":
            for position, name in enumerate(sorted(contacts), start=1):
                print(f"{position}. {name}: {contacts[name]}")
        case "quit":
            print("Goodbye!")
            break
        case _:
            print("Unknown command. Try add, find, delete, list, or quit.")

In the editor, press Escape then Tab to move on.

One answer per line, given to input() in order.

መፍትሄውን አሳይ

This is one way to solve it, not the only one. If yours prints the same thing, it works.

contacts = {"Selam": "0900 000 003", "Abel": "0900 000 001", "Hana": "0900 000 002"}
while True:
    command = input("Command: ")
    match command:
        case "add":
            name = input("Name: ")
            number = input("Number: ")
            if name in contacts:
                print(f"Updated {name}.")
            else:
                print(f"Saved {name}.")
            contacts[name] = number
        case "find":
            name = input("Name: ")
            if name in contacts:
                print(f"{name}: {contacts[name]}")
            else:
                print(f"No contact called {name}.")
        case "list":
            for position, name in enumerate(sorted(contacts), start=1):
                print(f"{position}. {name}: {contacts[name]}")
        case "quit":
            print("Goodbye!")
            break
        case _:
            print("Unknown command. Try add, find, delete, list, or quit.")

Notice the order in add: the check comes before saving. After saving, the name is always in the dict, so every contact would say "Updated".

Milestone 4: delete, safely

del contacts[name] removes a contact, but on a name that isn't there it stops the whole program:

Also make list say so when the book is empty: an empty dict is falsy (lesson 2.4), so if not contacts: is true exactly when there are none.

መልመጃ

Delete with checks

Add delete: ask Name: , then print Deleted Selam. or No contact called Yonas. Make list print No contacts yet. when the dict is empty.

Your code

contacts = {"Selam": "0900 000 003", "Abel": "0900 000 001", "Hana": "0900 000 002"}
while True:
    command = input("Command: ")
    match command:
        case "add":
            name = input("Name: ")
            number = input("Number: ")
            if name in contacts:
                print(f"Updated {name}.")
            else:
                print(f"Saved {name}.")
            contacts[name] = number
        case "find":
            name = input("Name: ")
            if name in contacts:
                print(f"{name}: {contacts[name]}")
            else:
                print(f"No contact called {name}.")
        case "list":
            for position, name in enumerate(sorted(contacts), start=1):
                print(f"{position}. {name}: {contacts[name]}")
        case "quit":
            print("Goodbye!")
            break
        case _:
            print("Unknown command. Try add, find, delete, list, or quit.")

In the editor, press Escape then Tab to move on.

One answer per line, given to input() in order.

መፍትሄውን አሳይ

This is one way to solve it, not the only one. If yours prints the same thing, it works.

contacts = {"Selam": "0900 000 003", "Abel": "0900 000 001", "Hana": "0900 000 002"}
while True:
    command = input("Command: ")
    match command:
        case "add":
            name = input("Name: ")
            number = input("Number: ")
            if name in contacts:
                print(f"Updated {name}.")
            else:
                print(f"Saved {name}.")
            contacts[name] = number
        case "find":
            name = input("Name: ")
            if name in contacts:
                print(f"{name}: {contacts[name]}")
            else:
                print(f"No contact called {name}.")
        case "delete":
            name = input("Name: ")
            if name in contacts:
                del contacts[name]
                print(f"Deleted {name}.")
            else:
                print(f"No contact called {name}.")
        case "list":
            if not contacts:
                print("No contacts yet.")
            for position, name in enumerate(sorted(contacts), start=1):
                print(f"{position}. {name}: {contacts[name]}")
        case "quit":
            print("Goodbye!")
            break
        case _:
            print("Unknown command. Try add, find, delete, list, or quit.")

That's the complete contact book. Test it the way a user would break it: delete someone twice, find a name with a typo, type a command in capitals.

Milestone 5: several numbers, partial search (stretch)

People have more than one number. Make each value a list of numbers: a dict of lists. add on an existing name appends to that person's list instead of replacing it. Create the empty list first if the name is new.

And find can accept part of a name: in works on text too, so "el" in "Abel" is True. Use a comprehension (lesson 4.7) to collect the matching names. The match is case-sensitive for now; Module 6 shows how to ignore case.

መልመጃ

A dict of lists (stretch)

Store numbers as lists. add appends and prints Abel has 2 numbers. (or 1 number). find asks Name or part of it: and prints every number of every matching contact, or No contact matches Zed. list prints each name numbered, with its numbers indented on the lines below.

Your code

contacts = {"Selam": ["0900 000 003"], "Abel": ["0900 000 001"], "Hana": ["0900 000 002"]}
while True:
    command = input("Command: ")
    match command:
        case "add":
            name = input("Name: ")
            number = input("Number: ")
            if name in contacts:
                print(f"Updated {name}.")
            else:
                print(f"Saved {name}.")
            contacts[name] = number
        case "find":
            name = input("Name: ")
            if name in contacts:
                print(f"{name}: {contacts[name]}")
            else:
                print(f"No contact called {name}.")
        case "delete":
            name = input("Name: ")
            if name in contacts:
                del contacts[name]
                print(f"Deleted {name}.")
            else:
                print(f"No contact called {name}.")
        case "list":
            if not contacts:
                print("No contacts yet.")
            for position, name in enumerate(sorted(contacts), start=1):
                print(f"{position}. {name}: {contacts[name]}")
        case "quit":
            print("Goodbye!")
            break
        case _:
            print("Unknown command. Try add, find, delete, list, or quit.")

In the editor, press Escape then Tab to move on.

One answer per line, given to input() in order.

መፍትሄውን አሳይ

This is one way to solve it, not the only one. If yours prints the same thing, it works.

contacts = {"Selam": ["0900 000 003"], "Abel": ["0900 000 001"], "Hana": ["0900 000 002"]}
while True:
    command = input("Command: ")
    match command:
        case "add":
            name = input("Name: ")
            number = input("Number: ")
            if name not in contacts:
                contacts[name] = []
            contacts[name].append(number)
            count = len(contacts[name])
            if count == 1:
                print(f"{name} has 1 number.")
            else:
                print(f"{name} has {count} numbers.")
        case "find":
            part = input("Name or part of it: ")
            matches = [name for name in sorted(contacts) if part in name]
            if not matches:
                print(f"No contact matches {part}.")
            for name in matches:
                for number in contacts[name]:
                    print(f"{name}: {number}")
        case "delete":
            name = input("Name: ")
            if name in contacts:
                del contacts[name]
                print(f"Deleted {name}.")
            else:
                print(f"No contact called {name}.")
        case "list":
            if not contacts:
                print("No contacts yet.")
            for position, name in enumerate(sorted(contacts), start=1):
                print(f"{position}. {name}")
                for number in contacts[name]:
                    print(f"   {number}")
        case "quit":
            print("Goodbye!")
            break
        case _:
            print("Unknown command. Try add, find, delete, list, or quit.")

ዋና ዋና ነጥቦች

  • A dict is the right shape for "find the number by name"; a dict of lists holds several numbers per name.
  • Check with in before reading or deleting a key, so a typo never ends the program with a KeyError.
  • match inside while True makes a menu; break in a case leaves the loop, not just the match.
  • enumerate(sorted(contacts), start=1) gives a numbered, alphabetical list.
  • Build in milestones and run after each one: list, menu, add and find, delete, then extras.