Dictionaries#

A dictionary stores key-value pairs, allowing fast lookup of a value by its key:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Create a dictionary
person = {
    "name": "Alice",
    "age": 25,
    "city": "Taipei"
}

# Empty dictionary
empty = {}
empty2 = dict()

# Create with dict()
profile = dict(name="Bob", age=30)

Dictionary characteristics:

  • Ordered (insertion order is preserved in Python 3.7+)
  • Unique keys (duplicate keys overwrite the previous value)
  • Keys must be hashable (strings, numbers, and tuples are valid; lists are not)

Accessing Elements#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
person = {"name": "Alice", "age": 25, "city": "Taipei"}

# Access value by key
print(person["name"])  # Alice
print(person["age"])   # 25

# Missing key raises KeyError
# print(person["email"])  # ❌ KeyError

# Use get(): returns None or a default value when key is missing
print(person.get("email"))            # None
print(person.get("email", "N/A"))     # N/A

Modifying a Dictionary#

Add and update#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
person = {"name": "Alice", "age": 25}

# Update an existing value
person["age"] = 26
print(person)  # {'name': 'Alice', 'age': 26}

# Add a new key-value pair
person["city"] = "Taipei"
print(person)  # {'name': 'Alice', 'age': 26, 'city': 'Taipei'}

# update(): batch update
person.update({"age": 27, "email": "alice@example.com"})
print(person)
# {'name': 'Alice', 'age': 27, 'city': 'Taipei', 'email': 'alice@example.com'}

Delete#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
person = {"name": "Alice", "age": 25, "city": "Taipei"}

# pop(): remove and return value
age = person.pop("age")
print(age)     # 25
print(person)  # {'name': 'Alice', 'city': 'Taipei'}

# pop() accepts a default to avoid KeyError
result = person.pop("email", "not found")
print(result)  # not found

# del: direct deletion
del person["city"]
print(person)  # {'name': 'Alice'}

# clear(): clear the dictionary
person.clear()
print(person)  # {}

Iterating Over a Dictionary#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
scores = {"Alice": 85, "Bob": 92, "Charlie": 78}

# Iterate over keys
for name in scores:
    print(name)

# Iterate over values
for score in scores.values():
    print(score)

# Iterate over key-value pairs (most common)
for name, score in scores.items():
    print(f"{name}: {score} points")

# Iterate over keys (explicit form)
for name in scores.keys():
    print(name)

Query Operations#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
person = {"name": "Alice", "age": 25, "city": "Taipei"}

# Check existence (checks keys by default)
print("name" in person)       # True
print("email" in person)      # False

# Check existence in values
print(25 in person.values())  # True

# Get all keys, values, and key-value pairs
print(list(person.keys()))    # ['name', 'age', 'city']
print(list(person.values()))  # ['Alice', 25, 'Taipei']
print(list(person.items()))   # [('name', 'Alice'), ('age', 25), ('city', 'Taipei')]

# Length
print(len(person))  # 3

setdefault() and the Counting Pattern#

setdefault(): set a default value when key is missing#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
person = {"name": "Alice"}

# Set key if missing, and return its value
email = person.setdefault("email", "unknown@example.com")
print(email)   # unknown@example.com
print(person)  # {'name': 'Alice', 'email': 'unknown@example.com'}

# Does not overwrite existing keys
person.setdefault("name", "Bob")
print(person["name"])  # Alice (not overwritten)

Word counting pattern#

1
2
3
4
5
6
7
8
text = "hello world hello python world hello"
word_count = {}

for word in text.split():
    word_count[word] = word_count.get(word, 0) + 1

print(word_count)
# {'hello': 3, 'world': 2, 'python': 1}

Dictionary Comprehension#

Dict comprehension syntax is similar to list comprehension, but uses curly braces and requires both a key and a value separated by a colon ::

{key_expr: value_expr for variable in iterable}
{key_expr: value_expr for variable in iterable if condition}

Basic form: build a dictionary from a sequence#

Traditional approach:

1
2
3
4
squares = {}
for x in range(1, 6):
    squares[x] = x ** 2
print(squares)  # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

Rewritten as a dict comprehension:

1
2
squares = {x: x ** 2 for x in range(1, 6)}
print(squares)  # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

With condition: keep only matching entries#

1
2
3
4
5
6
7
8
# Only even-number squares
even_squares = {x: x ** 2 for x in range(1, 11) if x % 2 == 0}
print(even_squares)  # {2: 4, 4: 16, 6: 36, 8: 64, 10: 100}

# Filter out failing students
scores = {"Alice": 85, "Bob": 55, "Charlie": 92, "David": 48}
passed = {name: score for name, score in scores.items() if score >= 60}
print(passed)  # {'Alice': 85, 'Charlie': 92}

Merge two lists into a dictionary#

Use zip() to pair a names list with a values list:

1
2
3
4
5
names  = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]

score_map = {name: score for name, score in zip(names, scores)}
print(score_map)  # {'Alice': 85, 'Bob': 92, 'Charlie': 78}

Transform values in an existing dictionary#

Apply an operation to every value while keeping the keys unchanged:

1
2
3
4
5
prices = {"apple": 30, "banana": 15, "cherry": 25}

# Apply a 20% discount to all prices
discounted = {item: price * 0.8 for item, price in prices.items()}
print(discounted)  # {'apple': 24.0, 'banana': 12.0, 'cherry': 20.0}

Invert a dictionary (swap keys and values)#

1
2
3
original = {"a": 1, "b": 2, "c": 3}
reversed_dict = {v: k for k, v in original.items()}
print(reversed_dict)  # {1: 'a', 2: 'b', 3: 'c'}

⚠️ The original values must be hashable (e.g. integers, strings) to become new keys. If values are duplicated, the later key overwrites the earlier one.


Nested Dictionaries#

A dictionary’s values can themselves be dictionaries:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
students = {
    "Alice": {"age": 20, "score": 85, "city": "Taipei"},
    "Bob":   {"age": 22, "score": 92, "city": "Kaohsiung"},
    "Charlie": {"age": 21, "score": 78, "city": "Taichung"},
}

# Access nested values
print(students["Alice"]["score"])  # 85

# Iterate
for name, info in students.items():
    print(f"{name} ({info['city']}): {info['score']} points")

Merging Dictionaries (Python 3.9+)#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
defaults = {"color": "blue", "size": "M", "quantity": 1}
custom = {"color": "red", "quantity": 5}

# Merge (custom values override defaults)
merged = defaults | custom
print(merged)
# {'color': 'red', 'size': 'M', 'quantity': 5}

# In-place update
defaults |= custom
print(defaults)
# {'color': 'red', 'size': 'M', 'quantity': 5}

Practical Examples#

Vote tally#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
votes = ["Alice", "Bob", "Alice", "Charlie", "Bob", "Alice", "Bob"]

tally = {}
for vote in votes:
    tally[vote] = tally.get(vote, 0) + 1

winner = max(tally, key=tally.get)
print(f"Vote counts: {tally}")
print(f"Winner: {winner}")
# Vote counts: {'Alice': 3, 'Bob': 3, 'Charlie': 1}

Grouping data#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
students = [
    {"name": "Alice", "city": "Taipei"},
    {"name": "Bob", "city": "Kaohsiung"},
    {"name": "Charlie", "city": "Taipei"},
    {"name": "David", "city": "Kaohsiung"},
]

by_city = {}
for s in students:
    city = s["city"]
    by_city.setdefault(city, []).append(s["name"])

for city, names in by_city.items():
    print(f"{city}: {', '.join(names)}")
# Taipei: Alice, Charlie
# Kaohsiung: Bob, David