Skip to content
elephantoo

Dictionaries

Lesson 10 of 38 12 min read

Key-value lookups, safe access, merging, looping, dict comprehensions, nested data, Counter and defaultdict.


A dictionary (dict) maps keys to values, like a real dictionary maps words to definitions. Instead of asking "what's at position 3?", you ask "what's the price of mango?" or "what's this user's email?". Dicts are fast, flexible and everywhere: JSON from web APIs, configuration files, database rows, caches and counting.

Creating dictionaries#

Python
user = {
    "name": "Ada",
    "email": "ada@example.com",
    "age": 36,
    "skills": ["maths", "programming"],
}
empty = {}
from_pairs = dict([("a", 1), ("b", 2)])
from_kwargs = dict(host="localhost", port=5432)

print(user["name"])
print(from_pairs, from_kwargs)
print(len(user))
Output
Ada
{'a': 1, 'b': 2} {'host': 'localhost', 'port': 5432}
4

Keys must be hashable — strings, numbers, tuples — and each key appears only once. Values can be anything, including lists and other dicts.

Reading values safely#

Square brackets raise KeyError when the key is missing. get() returns None or a default instead:

Python
user = {"name": "Ada", "age": 36}

print(user.get("email"))
print(user.get("email", "not provided"))
print("age" in user, "email" in user)   # `in` checks KEYS

print(user["email"])
Output
None
not provided
True False
Traceback (most recent call last):
  ...
KeyError: 'email'

Use [] when a missing key is a bug that should fail loudly; use get() when a missing key is normal.

Adding, updating and removing#

Python
stock = {"apple": 50, "banana": 20}

stock["mango"] = 15            # add a new key
stock["apple"] -= 5            # update an existing one
stock.update({"banana": 30, "kiwi": 8})   # merge in several
print(stock)

removed = stock.pop("kiwi")    # remove and return the value
del stock["banana"]            # remove without returning
print(removed, stock)

stock.setdefault("grape", 0)   # add only if missing
stock.setdefault("apple", 999) # apple exists, so unchanged
print(stock)
Output
{'apple': 45, 'banana': 30, 'mango': 15, 'kiwi': 8}
8 {'apple': 45, 'mango': 15}
{'apple': 45, 'mango': 15, 'grape': 0}

The merge operators (Python 3.9+) combine dicts; values from the right-hand side win:

Python
defaults = {"theme": "light", "font_size": 14}
user_prefs = {"theme": "dark"}

settings = defaults | user_prefs   # new dict
print(settings)
defaults |= {"lang": "en"}          # update in place
print(defaults)
Output
{'theme': 'dark', 'font_size': 14}
{'theme': 'light', 'font_size': 14, 'lang': 'en'}

Looping over dictionaries#

Python
prices = {"apple": 40, "banana": 10, "mango": 60}

for fruit in prices:                  # iterating a dict gives its keys
    print(fruit, end=" ")
print()

print(list(prices.values()))
for fruit, price in prices.items():   # key-value pairs
    print(f"{fruit:<7}₹{price}")

print("Cheapest:", min(prices, key=prices.get))
print(sorted(prices.items(), key=lambda kv: kv[1], reverse=True))
Output
apple banana mango 
[40, 10, 60]
apple  ₹40
banana ₹10
mango  ₹60
Cheapest: banana
[('mango', 60), ('apple', 40), ('banana', 10)]

Dictionaries remember insertion order (guaranteed since Python 3.7) — they don't sort themselves. min(prices, key=prices.get) is a neat idiom: it compares keys by their values.

Don't add or remove keys while iterating over a dict — Python raises RuntimeError. Loop over list(d) if you must.

Dictionary comprehensions#

Build dicts in one expression, just like list comprehensions:

Python
names = ["ada", "grace", "linus"]
lengths = {name: len(name) for name in names}
print(lengths)

prices = {"apple": 40, "banana": 10, "mango": 60}
expensive = {k: v for k, v in prices.items() if v >= 40}
inverted = {v: k for k, v in prices.items()}
print(expensive)
print(inverted)
Output
{'ada': 3, 'grace': 5, 'linus': 5}
{'apple': 40, 'mango': 60}
{40: 'apple', 10: 'banana', 60: 'mango'}

Nested data#

Real data — especially JSON from APIs — is dicts inside lists inside dicts:

Python
order = {
    "id": 1042,
    "customer": {"name": "Ada", "city": "Pune"},
    "items": [
        {"sku": "KB-01", "qty": 1, "price": 2499},
        {"sku": "MS-02", "qty": 2, "price": 799},
    ],
}

print(order["customer"]["city"])
total = sum(item["qty"] * item["price"] for item in order["items"])
print(f"Order {order['id']} total: ₹{total:,}")
print(order.get("shipping", {}).get("method", "standard"))
Output
Pune
Order 1042 total: ₹4,097
standard

The chained .get(..., {}) pattern safely digs into optional nested keys.

Counting and grouping#

Two jobs come up constantly: counting things and grouping things. You can do them with plain dicts:

Python
words = "the cat and the hat and the bat".split()

counts = {}
for w in words:
    counts[w] = counts.get(w, 0) + 1
print(counts)
Output
{'the': 3, 'cat': 1, 'and': 2, 'hat': 1, 'bat': 1}

But the collections module has purpose-built tools:

Python
from collections import Counter, defaultdict

words = "the cat and the hat and the bat".split()
counts = Counter(words)
print(counts.most_common(2))

by_letter = defaultdict(list)       # missing keys start as []
for w in words:
    by_letter[w[0]].append(w)
print(dict(by_letter))
Output
[('the', 3), ('and', 2)]
{'t': ['the', 'the', 'the'], 'c': ['cat'], 'a': ['and', 'and'], 'h': ['hat'], 'b': ['bat']}

Dict views#

keys(), values() and items() return live views, not copies — they reflect later changes. Key views even support set operations:

Python
a = {"x": 1, "y": 2}
b = {"y": 3, "z": 4}
keys = a.keys()
a["w"] = 0
print(list(keys))
print(a.keys() & b.keys())
Output
['x', 'y', 'w']
{'y'}

Worked example: a word-frequency report#

Python
from collections import Counter

text = """Python is great. Python is readable, and readable code
is maintainable code. Great code is readable."""

cleaned = "".join(ch.lower() if ch.isalpha() or ch.isspace() else " " for ch in text)
counts = Counter(cleaned.split())

print(f"{len(counts)} distinct words")
for word, n in counts.most_common(4):
    print(f"{word:<10}{'#' * n} {n}")
Output
7 distinct words
is        #### 4
readable  ### 3
code      ### 3
python    ## 2

Common mistakes#

  • KeyError on missing keys — use get(), in, or setdefault/defaultdict.
  • Using a list as a key — convert it to a tuple.
  • Expecting dicts to be sorted — they keep insertion order; sort explicitly with sorted(d.items()).
  • Mutating a dict while looping over it — iterate over list(d.items()) instead.
  • for k in d.keys() — just write for k in d.

What's next#

With lists, tuples, sets and dicts, you have Python's full toolkit of built-in data structures. Next we'll organise code itself into reusable pieces with functions.

Check your understanding

Quick quiz

0/3 answered
  1. 1.What does user.get("email", "n/a") return if user has no "email" key?

  2. 2.Since Python 3.7, what order do you get when iterating over a dict?

  3. 3.Which is the most Pythonic way to count word occurrences in a list words?

Finished reading?

Mark this lesson complete to track your progress.