Dictionaries
Dictionaries are used to store data as key-value pairs. They are like a real-world dictionary where we look up a word (the key) to find its definition (the value). Keys must be immutable (strings, integers, tuples), while values can be anything.
Creating and Accessing Dictionaries
Dictionaries are O(1) fast for lookups and insertions. Since Python 3.7, they keep the order in which we add items.
# Type hinting: specify Key and Value types inside dict[]
# See: [Modern Python Types](../../1 - basics/1.9 - modern-python-types.md) for details
car: dict[str, str | int] = {"brand": "Tesla", "model": "S", "year": 2022}
# Grabbing a value by its key
print(car["brand"])
Output:
Tesla
The .get() Trick
If we try to access a key that doesn't exist (like car["color"]), Python will crash with a KeyError. Using .get() is safer because it returns None (or a default fallback) instead of crashing.
# Safe way to look up a key
print(car.get("color"))
# We can even provide a default fallback
print(car.get("color", "Not Available"))
None
Not Available
Modifying Dictionaries
Dictionaries are mutable, so we can add, change, or remove items on the fly.
# Adding or changing items
car["color"] = "Red"
car.update({"year": 2024}) # Updates existing or adds if not found
# Removing items
# pop() removes and returns the value
popped_year = car.pop("year")
print(car)
print(popped_year)
{'brand': 'Tesla', 'model': 'S', 'color': 'Red'}
2024
Merging Dictionaries (Python 3.9+)
Instead of using update(), which changes the original dictionary, we can use the | (merge) operator to create a brand-new one. The right-side value wins if there's a collision (the second dictionary's value overrides the first).
A = {"x": 1, "y": 2}
B = {"y": 3, "z": 4}
print(A | B)
{'x': 1, 'y': 3, 'z': 4}
Unlike sets, dictionaries do not support the bitwise AND (&) operator directly. To find common keys, we would need to intersect the sets of their keys: a.keys() & b.keys().
Worth Mentioning: Initialization and Bulk Creation
setdefault(): Returns the value of a key if it exists; if not, inserts the key with a specified value. Great for initializing nested structures.fromkeys(): Creates a new dictionary with keys from a collection and a single value for all.clear(): Removes all items from the dictionary.
# setdefault: helpful for counters
counts = {"apples": 10}
counts.setdefault("oranges", 0) # Adds oranges with 0
counts.setdefault("apples", 0) # Does nothing (apples exists)
# fromkeys: bulk creation
users = dict.fromkeys(["alice", "bob", "charlie"], "standard")
print(counts)
print(users)
# Worth noting: .items() returns a view object (dict_items)
# which is an iterable of (key, value) tuples.
print(users.items())
Output:
{'apples': 10, 'oranges': 0}
{'alice': 'standard', 'bob': 'standard', 'charlie': 'standard'}
dict_items([('alice', 'standard'), ('bob', 'standard'), ('charlie', 'standard')])