dict: Key and Value

Lesson goals
  • Create a dict and read a value by key
  • Add, update and delete key-value pairs
  • Use get with a default to avoid a KeyError
  • Loop over a dictionary with keys, values and items, and work with a list of dictionaries

What will we learn?

A dict (Dictionary) is a structure that holds pairs of a key and a value. Instead of accessing by position ([0]), you access by name (["name"]).

Why do we need this?

In the tuple ("Dana", 24, "Haifa") you have to remember that position 1 is the age. With 15 fields, that is a recipe for bugs. A dict solves this:

student = {"name": "Dana", "age": 24, "city": "Haifa"}
student["age"]   # clear and readable

dict is perhaps the most important structure for the coming weeks:

  • JSON (tomorrow): the format in which files are saved and information is sent over the network. In Python it becomes a dict.
  • The backend and databases topics: every request and response in an API is essentially a dict. {"id": 7, "name": "Dana"}.
  • Every “record”, like a user, a product or an order, is convenient to represent as a dict.

Explanation from the basics

What is a dict?

  • Every item is a pair: a Key → a Value.
  • Unique keys — there are no two identical keys. Assigning to an existing key replaces the value.
  • Mutable.
  • Keeps the insertion order (since Python 3.7).
  • A key is usually a str or an int. A value can be anything: a number, text, a list, and even another dict.
Analogy: a dictionary / a phone book

In a real dictionary you look up a word (the key) and get a definition (the value). You don’t look for “the definition at position 5,000 in the book”. In a phone book: name → number. Two people cannot appear with exactly the same name as separate entries. A new entry with the same name replaces the previous one.

Syntax

# create
empty = {}
student = {"name": "Dana", "age": 24}

# read
student["name"]                 # "Dana" - KeyError if the key does not exist
student.get("email")            # None - no error if missing
student.get("email", "N/A")     # "N/A" - default value if missing

# add / update (same syntax!)
student["email"] = "dana@mail.com"   # new key -> added
student["age"] = 25                  # existing key -> updated

# delete
del student["email"]            # delete a key (KeyError if missing)
age = student.pop("age")        # delete and return the value

# check
"name" in student               # True - checks KEYS, not values
len(student)                    # number of key-value pairs

# iterate
for key in student:             # keys
for value in student.values():  # values
for key, value in student.items():   # pairs (tuples!)

First code example

week-2/student_dict.py:

student = {"name": "Dana", "age": 24, "city": "Haifa"}

print("Name:", student["name"])
print("Age:", student["age"])

student["age"] = 25
student["email"] = "dana@mail.com"
print("Updated:", student)

del student["city"]
print("After delete:", student)

print("Has email?", "email" in student)
print("Phone:", student.get("phone", "not provided"))

Output:

Name: Dana
Age: 24
Updated: {'name': 'Dana', 'age': 25, 'city': 'Haifa', 'email': 'dana@mail.com'}
After delete: {'name': 'Dana', 'age': 25, 'email': 'dana@mail.com'}
Has email? True
Phone: not provided

Breaking down the code line by line

LineExplanation
{"name": "Dana", ...}A key, a colon, a value. Pairs are separated by commas.
student["name"]Reading by key: the value "Dana".
student["age"] = 25The key exists, so the value is updated.
student["email"] = "..."The key does not exist, so it is added (at the end). Exactly the same syntax!
del student["city"]Deleting the whole pair.
"email" in studentChecks whether the key exists.
student.get("phone", "not provided")The key does not exist, so the default is returned, without an error.

Another example

A list of dictionaries — the most common structure for “real” data (a table: every dict is a row). And also counting with a dict. week-2/products.py:

products = [
    {"id": 1, "name": "Laptop", "price": 3500, "category": "electronics"},
    {"id": 2, "name": "Mouse", "price": 80, "category": "electronics"},
    {"id": 3, "name": "Desk", "price": 900, "category": "furniture"},
    {"id": 4, "name": "Chair", "price": 450, "category": "furniture"},
]

print("--- Catalog ---")
for product in products:
    print(product["id"], product["name"], product["price"], "NIS")

expensive = []
for product in products:
    if product["price"] > 500:
        expensive.append(product["name"])
print("Over 500 NIS:", expensive)

count_by_category = {}
for product in products:
    category = product["category"]
    count_by_category[category] = count_by_category.get(category, 0) + 1
print("Count by category:", count_by_category)

for category, count in count_by_category.items():
    print(category, "->", count, "products")

Output:

--- Catalog ---
1 Laptop 3500 NIS
2 Mouse 80 NIS
3 Desk 900 NIS
4 Chair 450 NIS
Over 500 NIS: ['Laptop', 'Desk']
Count by category: {'electronics': 2, 'furniture': 2}
electronics -> 2 products
furniture -> 2 products

Breakdown:

  • products is a list where every item is a dict. product["price"] = the price of the current product in the loop.
  • The counting pattern: count_by_category.get(category, 0) + 1. If the category is not in the dictionary yet, get returns 0, and then 0+1 = 1. If it is already there, you get the existing count and add 1. This is a pattern that comes up again and again.
  • .items() — returns (key, value) pairs as tuples, and for category, count in unpacks them (Unpacking from the previous lesson).

A dict inside a dict

A value can be a dict itself:

user = {
    "name": "Dana",
    "address": {"city": "Haifa", "street": "Herzl 10"},
    "hobbies": ["chess", "running"],
}
print(user["address"]["city"])   # Haifa
print(user["hobbies"][0])        # chess

You access it “layer by layer”: user["address"] returns a dict, and on it ["city"]. That is exactly the structure of JSON.

Common mistake

Mistake 1: a KeyError on a key that does not exist.

student = {"name": "Dana"}
print(student["age"])
KeyError: 'age'

The fix: student.get("age") (returns None), student.get("age", 0) (a default), or check first with if "age" in student:.

Mistake 2: searching for a value with in.

student = {"name": "Dana"}
print("Dana" in student)
False

in on a dict checks keys. To check values: "Dana" in student.values().

Mistake 3: a typo in a key, which creates a new key instead of updating.

student = {"name": "Dana", "age": 24}
student["Age"] = 25
print(student)
{'name': 'Dana', 'age': 24, 'Age': 25}

No error! A new key "Age" was simply added, and "age" stayed 24. Keys are case-sensitive. It is a silent Logical Error, so keep the key names consistent (snake_case).

Mistake 4: a list as a key.

locations = {[32, 35]: "Haifa"}
TypeError: unhashable type: 'list'

A key must be Immutable. If you need a key made of several values, use a tuple: {(32, 35): "Haifa"}. (We will explain the connection to Immutable in the next lesson.)

Practice

The exercises use only what was taught in this lesson and the lessons before it. Try on your own first, and only then open the solution.

Exercise 1: A student card

Create a dictionary student with the name "Dana", the age 24 and the city "Haifa". Then:

  1. Update the age to 25.
  2. Add an email "dana@mail.com".
  3. Delete the city.
  4. Print the dictionary, and the phone using get with the default "none".

Expected output:

{'name': 'Dana', 'age': 25, 'email': 'dana@mail.com'}
none
Show solution
student = {"name": "Dana", "age": 24, "city": "Haifa"}

student["age"] = 25
student["email"] = "dana@mail.com"
del student["city"]

print(student)
print(student.get("phone", "none"))
{'name': 'Dana', 'age': 25, 'email': 'dana@mail.com'}
none

student["phone"] would throw a KeyError. get returns a default value when the key does not exist.

Exercise 2: Counting words

Write a function count_words(text) that returns a dictionary: every word (in lowercase) → how many times it appears.

counts = count_words("the cat and the dog and THE bird")
print(counts)

Expected output:

{'the': 3, 'cat': 1, 'and': 2, 'dog': 1, 'bird': 1}
Show solution
def count_words(text):
    counts = {}
    for word in text.lower().split():
        counts[word] = counts.get(word, 0) + 1
    return counts


counts = count_words("the cat and the dog and THE bird")
print(counts)
{'the': 3, 'cat': 1, 'and': 2, 'dog': 1, 'bird': 1}

counts.get(word, 0) returns 0 for a word that was not counted yet — that way you do not need a separate if for a new word.

Exercise 3: A list of dictionaries

products = [
    {"name": "Laptop", "price": 3500, "category": "electronics"},
    {"name": "Mouse", "price": 80, "category": "electronics"},
    {"name": "Desk", "price": 900, "category": "furniture"},
]
  1. Print the names of the products whose price is below 1000.
  2. Build a dictionary that counts how many products there are in each category, and print it.

Expected output:

Mouse
Desk
{'electronics': 2, 'furniture': 1}
Show solution
products = [
    {"name": "Laptop", "price": 3500, "category": "electronics"},
    {"name": "Mouse", "price": 80, "category": "electronics"},
    {"name": "Desk", "price": 900, "category": "furniture"},
]

for product in products:
    if product["price"] < 1000:
        print(product["name"])

counts = {}
for product in products:
    category = product["category"]
    counts[category] = counts.get(category, 0) + 1
print(counts)
Mouse
Desk
{'electronics': 2, 'furniture': 1}

Exercise 4: Looping over a dictionary

stock = {"apple": 12, "banana": 0, "cherry": 5}

Loop over the dictionary with items() and print every item and its quantity. For an item whose quantity is 0, print out of stock.

Expected output:

apple -> 12
banana -> out of stock
cherry -> 5
Show solution
stock = {"apple": 12, "banana": 0, "cherry": 5}

for name, quantity in stock.items():
    if quantity == 0:
        print(name, "->", "out of stock")
    else:
        print(name, "->", quantity)
apple -> 12
banana -> out of stock
cherry -> 5

items() returns pairs of a key and a value, and Unpacking in the loop splits each pair into two variables.

Exercise 5: A nested dictionary

user = {
    "name": "Dana",
    "address": {"city": "Haifa", "street": "Herzl"},
    "hobbies": ["chess", "running"],
}

Print the city, the first hobby, and add a hobby "music". At the end, print the list of hobbies.

Expected output:

Haifa
chess
['chess', 'running', 'music']
Show solution
user = {
    "name": "Dana",
    "address": {"city": "Haifa", "street": "Herzl"},
    "hobbies": ["chess", "running"],
}

print(user["address"]["city"])
print(user["hobbies"][0])
user["hobbies"].append("music")
print(user["hobbies"])
Haifa
chess
['chess', 'running', 'music']

Every [ ] goes one level deeper: user["address"] is a dictionary, and ["city"] takes a value out of it. That is exactly the structure of JSON.

Summary

  • dict {key: value} — access by key and not by position. Keys are unique.
  • d[key] = value — adds if new, updates if it exists.
  • d[key] throws a KeyError on a missing key. d.get(key, default) does not throw.
  • in checks keys. Looping: keys(), values(), items() (pairs).
  • A list of dictionaries = a table. A dict inside a dict = JSON.
  • The counting pattern: counts[x] = counts.get(x, 0) + 1.

Official documentation