Python dictionaries: keys, values, methods and comprehensions
Advertisement
A dictionary stores key-value pairs: you look something up by a name instead of a position. Phone books, settings, product records, the JSON your favourite web service returns: all of these are dictionaries in Python. Lookups are very fast, and dictionaries keep the order in which keys were added. This post shows how to create one, read and change it safely, loop over it, merge it, nest it, count things with it and build one with a comprehension. All outputs were produced on Python 3.9.
Creating a dictionary
Use curly brackets with key: value pairs, or the dict() function:
person = {"name": "Asha", "age": 30, "city": "Pune"}
empty = {}
print(person, len(person), empty)
# {'name': 'Asha', 'age': 30, 'city': 'Pune'} 3 {}
print(dict(a=1, b=2), dict([("x", 1), ("y", 2)]), dict.fromkeys(["a", "b"], 0))
# {'a': 1, 'b': 2} {'x': 1, 'y': 2} {'a': 0, 'b': 0}
print(dict(zip(["k1", "k2"], [10, 20]))) # {'k1': 10, 'k2': 20}
Rules to remember:
- Keys must be unique. If you repeat a key, the later value wins.
- Keys must be hashable, which in practice means immutable: strings, numbers and tuples work; lists, sets and dictionaries do not.
- Values can be anything, including other dictionaries.
print({"a": 1, "a": 2}) # {'a': 2}
print({(1, 2): "point", 5: "five", "k": [1, 2]})
try:
{[1]: "list as key"}
except TypeError as e:
print(e) # unhashable type: 'list'
Reading values
Square brackets look a key up and raise KeyError if it is missing. get() returns None (or a default you choose) instead.
person = {"name": "Asha", "age": 30, "city": "Pune"}
print(person["name"], person.get("age"), person.get("phone"), person.get("phone", "n/a"))
# Asha 30 None n/a
try:
person["phone"]
except KeyError as e:
print("KeyError:", e) # KeyError: 'phone'
Use [] when a missing key means a bug, and get() when absence is normal. To test for a key, use in, which checks keys, not values:
print("name" in person, "Asha" in person, "Asha" in person.values()) # True False True
Adding and updating
Assigning to a key adds it if it is new and replaces it if it exists.
person["phone"] = "98765" # add
person["age"] = 31 # update
person.update({"city": "Delhi", "email": "a@example.com"}) # several at once
print(person)
# {'name': 'Asha', 'age': 31, 'city': 'Delhi', 'phone': '98765', 'email': 'a@example.com'}
setdefault(key, default) returns the value if the key exists; otherwise it stores the default and returns it. It is handy for grouping:
groups = {}
for word in ["apple", "avocado", "banana", "blueberry", "cherry"]:
groups.setdefault(word[0], []).append(word)
print(groups)
# {'a': ['apple', 'avocado'], 'b': ['banana', 'blueberry'], 'c': ['cherry']}
Deleting
| Code | Effect |
|---|---|
del d[key] |
removes the key; KeyError if missing |
d.pop(key) |
removes it and returns the value; KeyError if missing |
d.pop(key, default) |
same, but returns the default instead of raising |
d.popitem() |
removes and returns the last added pair |
d.clear() |
empties the dictionary |
d = {"a": 1, "b": 2, "c": 3, "d": 4}
del d["a"]
print(d.pop("b"), d.pop("zzz", "none"), d.popitem(), d) # 2 none ('d', 4) {'c': 3}
d.clear()
print(d) # {}
Looping
Looping over a dictionary gives its keys. Use .values() for values and .items() for pairs:
prices = {"tea": 20, "coffee": 30, "milk": 25}
for key in prices:
print(key, end=" ") # tea coffee milk
print()
print(list(prices.keys()), list(prices.values()))
# ['tea', 'coffee', 'milk'] [20, 30, 25]
for item, price in prices.items():
print(f"{item:<8}{price:>4}")
tea 20
coffee 30
milk 25
keys(), values() and items() return views: live windows onto the dictionary, not copies. If the dictionary changes, the view changes too. Do not add or remove keys while looping over a dictionary; Python raises RuntimeError: dictionary changed size during iteration. Loop over list(d) if you need to change it as you go.
Sorting a dictionary
Dictionaries have no sort(); you sort their items and, if you want, build a new dictionary:
print(sorted(prices)) # ['coffee', 'milk', 'tea']
print(sorted(prices.items(), key=lambda kv: kv[1], reverse=True))
# [('coffee', 30), ('milk', 25), ('tea', 20)]
print(max(prices, key=prices.get), min(prices.values())) # coffee 20
print(dict(sorted(prices.items(), key=lambda kv: kv[1])))
# {'tea': 20, 'milk': 25, 'coffee': 30}
Merging and copying
Python 3.9 added the | operator for merging; update() and ** unpacking work on older versions too. When both dictionaries have a key, the right-hand value wins.
a = {"x": 1, "y": 2}
b = {"y": 20, "z": 30}
print(a | b) # {'x': 1, 'y': 20, 'z': 30}
print({**a, **b}) # {'x': 1, 'y': 20, 'z': 30}
c = a.copy() # shallow copy
c["x"] = 100
print(a, c) # {'x': 1, 'y': 2} {'x': 100, 'y': 2}
b = a does not copy; both names refer to one dictionary, as with lists. copy() is shallow, so nested dictionaries are still shared; use copy.deepcopy for full independence.
Nested dictionaries
Values can be dictionaries or lists, which is how structured data like JSON is represented:
students = {
"asha": {"marks": [88, 92], "city": "Pune"},
"ravi": {"marks": [75, 80], "city": "Delhi"},
}
print(students["asha"]["marks"][1]) # 92
students["ravi"]["marks"].append(90)
for name, info in students.items():
print(name, info["city"], sum(info["marks"]) / len(info["marks"]))
asha Pune 90.0
ravi Delhi 81.66666666666667
When a key might be missing at some level, chain get() calls: students.get("meena", {}).get("city", "unknown") gives unknown instead of an error. The JSON Formatter is useful for reading deeply nested data.
Counting and grouping
Counting how often things occur is a classic dictionary job:
text = "the quick the lazy the end"
counts = {}
for word in text.split():
counts[word] = counts.get(word, 0) + 1
print(counts) # {'the': 3, 'quick': 1, 'lazy': 1, 'end': 1}
from collections import Counter, defaultdict
c = Counter(text.split())
print(c.most_common(2)) # [('the', 3), ('quick', 1)]
dd = defaultdict(list)
for w in text.split():
dd[len(w)].append(w)
print(dict(dd))
# {3: ['the', 'the', 'the', 'end'], 5: ['quick'], 4: ['lazy']}
Counter does the counting for you, and defaultdict creates a missing value automatically, so you do not need setdefault or an if key in d check.
Dictionary comprehensions
A comprehension builds a dictionary from a loop: {key: value for item in iterable if condition}.
print({n: n * n for n in range(1, 5)}) # {1: 1, 2: 4, 3: 9, 4: 16}
prices = {"tea": 20, "coffee": 30, "milk": 25}
print({k: v for k, v in prices.items() if v >= 25}) # {'coffee': 30, 'milk': 25}
print({v: k for k, v in prices.items()}) # {20: 'tea', 30: 'coffee', 25: 'milk'}
print({k.upper(): v * 2 for k, v in prices.items()})
# {'TEA': 40, 'COFFEE': 60, 'MILK': 50}
Swapping keys and values (the third line) only works when the values are unique and hashable; duplicates collapse into one key.
Dictionaries compared with other types
| Need | Use |
|---|---|
| Look up a value by a name | dictionary |
| An ordered sequence you change | list |
| A fixed record | tuple or namedtuple |
| Unique items, fast membership | set |
A dictionary lookup takes about the same time however large it is, while searching a list takes longer as the list grows. That makes a dictionary the right tool for caches, indexes and anything you look up repeatedly.
Dictionaries in everyday code
Dictionaries are everywhere once you start looking. Function keyword arguments arrive as a dictionary (**kwargs), every object stores its attributes in one (obj.__dict__), JSON from a web service becomes nested dictionaries and lists when parsed with json.loads, and a module's global names live in a dictionary too. A common pattern is a lookup table that replaces a long chain of if/elif: map each choice to a function or a value, then fetch it with get and a default. Another is a cache: before doing slow work, check whether the answer for this input is already stored under that key. Because lookups are fast and keys keep insertion order, a dictionary often replaces both a list and a search loop.
Common mistakes
- Using
d[key]for a key that may not exist, instead ofd.get(key). - Using a list as a key.
- Expecting
into search values; it searches keys. - Changing the dictionary while looping over it.
- Using
d2 = d1and thinking you copied it. - Initialising many keys with a shared mutable value:
dict.fromkeys(keys, [])gives every key the same list. - Assuming a dictionary is sorted by key; it is in insertion order.
Practice
Read a sentence, count each word's frequency (ignoring case and punctuation) and print the three most common. Then build a phone book that supports adding, finding and deleting a name, with get for safe lookups. Finally, group a list of students by their city into a dictionary of lists.
Continue
Dictionaries pair well with functions (**kwargs is a dictionary), file handling (JSON) and object-oriented programming (every object keeps its attributes in a dictionary). The Python cheat sheet lists the dictionary methods on one page, and the Python video course covers the topic in order.