PythonBeginner 14 min Lesson 6 of 30

Day 6 — Dictionaries

Store data by name instead of position. Learn dictionary lookups, safe access with get, looping over items, and nesting for real-world records.

Python · Lesson 6 of 30
0/30 done(0%)

What is it? #

A dictionary stores values under names rather than positions. Instead of remembering that the email is at index 2, you ask for user["email"].

Each entry is a key and a value. Keys must be unique and hashable — strings, numbers and tuples work; lists do not. Values can be anything, including other dictionaries.

Lookups are fast no matter how many keys there are. Python hashes the key and jumps to the right slot, so a dictionary with a million entries answers about as quickly as one with ten.

Since Python 3.7, dictionaries remember the order keys were inserted. That is convenient for display, but you should still think of a dictionary as "lookup by name", not "list with labels".

Think of it like this #

A dictionary is a contacts app. You do not scroll to position 47 to find a phone number — you type the name. The name is the key, the number is the value, and each name appears once.

Simple example #

You are handling a user record. It has a name, an email, a role and a nested set of preferences. Some fields may be missing, so you need a way to read them without crashing.

Code #

PYTHON
user = {
    "name": "Ravi",
    "email": "[email protected]",
    "role": "editor",
    "prefs": {"theme": "dark", "emails": False},
}

print(user["name"])                       # Ravi
print(user.get("phone"))                  # None — no crash
print(user.get("phone", "not provided"))  # not provided

user["role"] = "admin"                    # update
user["last_login"] = "2026-09-22"         # add
del user["email"]                         # remove

print("role" in user)                     # True
print(list(user.keys()))
print(user["prefs"]["theme"])             # dark

for key, value in user.items():
    print(f"{key}: {value}")

# Counting with a dictionary
words = ["api", "db", "api", "cache", "api"]
counts = {}
for word in words:
    counts[word] = counts.get(word, 0) + 1
print(counts)                             # {'api': 3, 'db': 1, 'cache': 1}

How it works #

user["name"] looks up a key directly. If the key is missing you get a KeyError and the program stops, which is the right behaviour when the key is genuinely required.

user.get("phone") returns None instead of raising. The second argument sets a different fallback. Use [] when a missing key is a bug, and .get() when it is expected.

Assigning to a key that does not exist creates it; assigning to one that does replaces the value. There is no separate "add" and "update" operation.

del user["email"] removes a key. user.pop("email", None) does the same without raising when the key is absent.

"role" in user checks the keys, not the values — a detail worth remembering.

.items() gives you key and value together, which is why the loop can unpack into two names. .keys() and .values() exist for when you only need one side.

The counting block is the pattern you will reuse most. counts.get(word, 0) + 1 reads "whatever is there, or zero, plus one". It replaces an if-else and is the reason collections.Counter exists in the standard library for the same job.

Real-world use #

Dictionaries are the shape of almost all data that crosses a boundary. JSON from an API becomes a dictionary. A database row becomes a dictionary. Configuration files, form submissions, HTTP headers and environment variables all arrive as key-value pairs.

That makes .get() one of the most important habits in real Python. Data from outside your program is untrustworthy, and half of it will be missing a field you assumed was always there.

Counting and grouping with dictionaries is everywhere too: requests per endpoint, errors per type, sales per region. When your dictionary gets deeply nested, that is usually the signal to introduce a class or a dataclass instead, which you will meet on Day 22.

Common mistakes #

  • Using user["field"] on data from an API. External data is missing fields all the time — use .get() with a default.
  • Assuming "value" in my_dict checks values. It checks keys; use in my_dict.values() for values.
  • Trying to use a list as a key. Keys must be hashable, so convert to a tuple.
  • Changing a dictionary while looping over it. Iterate over list(d.keys()) if you need to add or delete during the loop.
  • Nesting five levels deep and then debugging it with print. At that depth, a class or dataclass is easier to work with.

Practice #

Build a dictionary describing a product (name, price, in stock). Print the price using a safe lookup with a default. Add a "category" key, remove one key, then loop over the dictionary printing key: value lines. Finally, count how often each word appears in "red blue red green blue red".

Quick quiz

  1. 1. What is the difference between `d["x"]` and `d.get("x")`?

  2. 2. Which of these cannot be a dictionary key?

  3. 3. What does `d.items()` give you when looping?

  4. 4. What does `counts[word] = counts.get(word, 0) + 1` do?

  5. 5. Why are dictionary lookups fast even with many keys?

Summary

  • Dictionaries store values under unique keys and look them up quickly.
  • Use `[]` when a key is required and `.get(key, default)` when it may be missing.
  • Assigning to a key both adds and updates — there is only one operation.
  • `.items()` is the normal way to loop over keys and values together.
  • Real-world data (JSON, rows, config) arrives as dictionaries, so defensive reads pay off.