Back to Blog
Python Dictionaries: Storing Data with Purpose

Python Dictionaries: Storing Data with Purpose

July 20, 2026
Updated August 17, 2026
Reynov Christian

You now know how to organize repeated logic into functions, and how to organize related items into lists. But what about data that's naturally labeled, like a contact's name, phone number, and email? Tracking those by position, “item 0 is the name, item 1 is the phone”, gets confusing fast, and no function can fix that on its own. That's exactly the problem Python dictionaries solve. They store data as clear key value pairs, so you always know what each piece of information means.

Creating and Accessing Dictionaries

contact = {
    "name": "Lely",
    "phone": "0812-3456-7890",
    "city": "Ruteng"
}

print(contact["name"])   # Lely
print(contact.get("email", "Not provided"))  # safe access with a default

A dictionary is written with curly braces. Each entry has a key, like "name", and a value, like "Lely", separated by a colon. You access data by key name, not by numeric position, which is far more readable than trying to remember that item 0 in a list means the name. Notice the two different ways to read a value here. Square brackets, contact["name"], work fine when you're sure the key exists. But .get("email", "Not provided") is safer, because it returns the given default value instead of crashing when the key is missing.

Essential Dictionary Methods

contact["email"] = "lely@example.com"   # add a new key
contact.update({"city": "Labuan Bajo"}) # update an existing key

print(contact.keys())    # all keys
print(contact.values())  # all values
print(contact.items())   # all key-value pairs, as tuples

del contact["phone"]     # remove a key

Just like lists, dictionaries are mutable. You can add, change, and remove entries after creating them. Assigning to a new key, like contact["email"] = ..., adds it if it doesn't exist yet, or overwrites it if it does. .items() is especially useful because it gives you both the key and the value together, which is exactly what you need when looping through a dictionary.

Dictionaries vs Lists: When to Use Each

  • Use a list when order matters and items are naturally accessed by position, like a queue of tasks or a sequence of scores.
  • Use a dictionary when each piece of data has a clear label and you look things up by name, like user profiles, configuration settings, or product details.

A helpful rule of thumb: if you ever catch yourself using a list and thinking “item 0 is always the name, item 1 is always the phone number”, that's usually a sign a dictionary would describe your data more clearly.

Nested Dictionaries

contacts = {
    "lely": {"phone": "0812-xxx", "city": "Ruteng"},
    "felix": {"phone": "0813-xxx", "city": "Kupang"}
}

print(contacts["lely"]["city"])  # Ruteng

A dictionary can hold other dictionaries as values. This is called nesting, and it lets you model real, structured data, like a whole address book instead of just one contact. To reach a value two levels deep, chain the square brackets one after another. Read contacts["lely"]["city"] from left to right: “in contacts, find the entry for lely, then inside that, find city.” This exact pattern, dictionaries nested inside dictionaries, is how most real databases and web APIs structure their information, so getting comfortable with it now will help you later.

To update a value at that deeper level, you follow the same chain: contacts["lely"]["city"] = "Labuan Bajo". Be careful accessing a key that might not exist at any level. If "budi" isn't in contacts, then contacts["budi"]["city"] raises a KeyError at the very first bracket, before Python even gets to the second one.

Dictionary Comprehensions

Just like list comprehensions, Python offers a compact way to build a new dictionary from an existing one:

prices = {"coffee": 15000, "tea": 10000, "water": 5000}
discounted = {item: price * 0.9 for item, price in prices.items()}
print(discounted)

This line means the same thing as writing a full loop by hand:

discounted = {}
for item, price in prices.items():
    discounted[item] = price * 0.9

Read the comprehension as “for every item and price pair in prices, create a new entry where the key stays item and the value becomes price * 0.9.” The result is a brand new dictionary with the same keys as prices, but with every value reduced by 10 percent. The original prices dictionary is left untouched.

Practical Project: Contact Management System

contacts = {}

while True:
    print("n1. Add contact  2. View all  3. Search  4. Delete  5. Exit")
    choice = input("Choose an option: ")

    if choice == "1":
        name = input("Name: ")
        phone = input("Phone: ")
        city = input("City: ")
        contacts[name] = {"phone": phone, "city": city}
        print(f"{name} added!")
    elif choice == "2":
        for name, info in contacts.items():
            print(f"{name}: {info['phone']} ({info['city']})")
    elif choice == "3":
        name = input("Search name: ")
        if name in contacts:
            print(contacts[name])
        else:
            print("Contact not found.")
    elif choice == "4":
        name = input("Delete which contact? ")
        if name in contacts:
            del contacts[name]
            print("Deleted.")
    elif choice == "5":
        print("Goodbye!")
        break

This project uses nested dictionaries, loops, and conditionals together. It's a strong preview of how real applications model structured data. A few lines deserve a closer look. In option 1, contacts[name] = {"phone": phone, "city": city} creates a dictionary inside a dictionary, using whatever the user typed as name as the outer key. This means every contact's own information is neatly grouped together under their name.

In option 2, info['phone'] and info['city'] use single quotes inside an f-string that's already wrapped in double quotes. This avoids a quote conflict. If you used double quotes on both the outer and inner level, Python would get confused about where the string actually ends. Options 3 and 4 both check if name in contacts first. This test looks at the dictionary's keys, and checking it before searching or deleting protects the program from crashing with a KeyError if the contact doesn't exist.

Common Beginner Mistakes

  • KeyError from a missing key: using square brackets on a key that doesn't exist, like contact["email"] when there's no email key, raises a KeyError. Use .get() with a default value if the key might be missing.
  • Forgetting nested access can fail at any level: in contacts["lely"]["city"], either "lely" or "city" could be missing. Check the outer key exists before chaining into the inner one.
  • Mixing up keys() and values(): looping with for key in contacts: already gives you the keys. You don't need .keys() unless you specifically want to store or print the full list of keys on its own.
  • Using a mutable value as a key: dictionary keys must be immutable, so a string or number works fine, but trying to use a list as a key raises a TypeError.

Try It Yourself

Practice these three short exercises in a new file called practice_dicts.py before moving on:

  1. Create a dictionary describing your favorite meal, with keys for name, price, and is_spicy. Print a sentence built from all three values.
  2. Write a dictionary comprehension that takes the prices dictionary from this article and creates a new dictionary containing only items that cost more than 8000.
  3. Add a 6th menu option to the Contact Management System that lets the user update just the phone number of an existing contact, without retyping their city.

Frequently Asked Questions

What happens if I access a key that doesn't exist?

Using square brackets, like contact["email"], raises a KeyError if the key is missing. Use .get("email", default_value) to avoid crashes and get a fallback value instead.

Can dictionary keys be numbers?

Yes, keys can be strings, numbers, or any immutable type. String keys are far more common though, since they're descriptive and easy to read later.

Are Python dictionaries ordered?

Yes. Since Python 3.7, dictionaries keep the order you inserted items in. Even so, you should generally rely on keys for lookup, not on position, since that's what dictionaries are designed for.

When should I nest dictionaries instead of using separate variables?

Whenever related data belongs together conceptually, like all the details of one contact or one product, nesting keeps that data organized and easy to pass around as a single unit.

What's the difference between del and pop() on a dictionary?

del contacts["lely"] removes the entry and returns nothing. contacts.pop("lely") removes the entry and returns its value, which is useful if you need to know what was removed right after deleting it.

Ready to build programs that handle unexpected situations gracefully? In our next article, we'll explore error handling in Python: try and except blocks, common exception types, and how to write robust code that doesn't crash when something goes wrong. You'll add professional grade input validation to your growing toolkit of projects.

Learning programming works best as a collaborative process, and your questions and insights help make these tutorials more effective for everyone. If you run into a KeyError you can't explain, get stuck nesting dictionaries, or discover an interesting feature to add to the contact management project, please share your experience in the comments. Your feedback helps create a learning community where we can all benefit from each other's discoveries and solutions.

You Might Also Like

5 Comments

W
WorkerTestAugust 1, 2026

Worker validation test real post

K
kontolAugust 2, 2026

mantap

T
TestAugust 2, 2026

Hello!

T
TestAugust 2, 2026

Hello from curl test!

F
FinalTestAugust 2, 2026

Final test after deploy

Leave a Comment

0/5000