Command Palette

Search for a command to run...

Hectal
PHASE 4Beginner ~27 min· topic 4 of 11

Topic 4.4

Dictionaries: Look Things Up by Key

In one line

A dictionary stores pairs: each key leads to a value, and you look a value up by its key instead of by position. Dictionaries keep the order in which keys were added (guaranteed since Python 3.7) and are the shape of almost all JSON data.

Think of it like this

A phone book or a school register. You don't count to the 57th entry; you look up a name and get a number. Python's dictionary (dict) works the same way: you give it a key, and it gives back the value stored for that key.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Dictionary (dict)
A collection of key-value pairs where you look up a value by its key.
Key
The label used to look something up. Keys are unique and must be hashable, meaning they never change (strings, numbers and tuples of them are fine; Topic 4.5 explains why).
Value
The data stored under a key. It can be any object.
Pair (item)
One key together with its value.
KeyError
The error raised when you read a key that is not in the dictionary.
View
The live result of keys(), values() or items(). It reflects later changes to the dictionary.
Insertion order
The order in which keys were first added. Python 3.7 and later keep it.
Merge
Combining two dictionaries into one. a | b is the Python 3.9 way.

Step by step

01Create a dictionary and look up a value

Square brackets with a key look up the value. This is not an index; there is no position number. The key can be a string, a number, or a tuple.

main.pywhole filepython
prices = {"apple": 30, "banana": 12, "mango": 50}
print(prices["apple"])      # 30
print(len(prices))          # 3
print(prices)
Create a dictionary and look up a valuediagram
Rendering diagram…

02Add, change and remove entries

The same assignment adds a new key or changes an existing one. pop removes a key and hands you its value; give pop a second argument to avoid a KeyError when the key might be missing.

main.pywhole filepython
prices = {"apple": 30, "banana": 12}
prices["kiwi"] = 40        # new key: added
prices["apple"] = 35       # existing key: replaced
removed = prices.pop("banana")
del prices["kiwi"]
print(prices, removed)

03Safe reading with get, in and setdefault

get never raises: a missing key gives None or your default. in tests keys. setdefault is a read that creates the entry if it was missing, which is useful for building groups. Topic 4.10 shows defaultdict, a neater tool for the same job.

main.pywhole filepython
prices = {"apple": 30}
print(prices.get("kiwi"))          # None
print(prices.get("kiwi", 0))       # 0
print("apple" in prices)           # True
print(30 in prices)                # False: 30 is a value, not a key
prices.setdefault("grape", 60)     # stores 60 and returns it
prices.setdefault("grape", 99)     # exists: keeps 60

04Loop over keys, values and items

A plain for over a dictionary gives keys. .items() gives (key, value) pairs, and tuple unpacking (Topic 4.3) splits each pair into two loop variables. The loop follows insertion order.

main.pywhole filepython
prices = {"apple": 30, "banana": 12}
for fruit in prices:
    print(fruit)
for price in prices.values():
    print(price)
for fruit, price in prices.items():
    print(fruit, "costs", price)

05Keys must be unique and hashable

Writing a key twice keeps the later value. A list cannot be a key, because it can change and a key must stay findable; Topic 4.5 explains why in full. Numbers, strings, booleans, None and tuples of those are fine.

main.pywhole filepython
d = {"a": 1, "a": 2}
print(d)                  # {'a': 2}
# d[[1, 2]] = "x"         # TypeError: unhashable type: 'list'

06Order and merging

Because of insertion order, print shows keys in the order you added them. A merge with | makes a new dictionary: first the left-hand pairs, then the right-hand ones, with the right-hand value replacing a left-hand value for a shared key. The merge operator needs Python 3.9 or newer.

main.pywhole filepython
defaults = {"theme": "light", "size": 12}
mine = {"size": 16}
settings = defaults | mine
print(settings)     # {'theme': 'light', 'size': 16}

07Dictionaries in the AI world

Every message you send to a chat model is a dictionary with a "role" (who is speaking) and a "content" (the text). The conversation is a list of those dictionaries. The reply you get back is also nested dictionaries and lists. Reading and building these is the everyday job of an AI engineer, and Topic 4.8 goes deeper.

main.pywhole filepython
conversation = [
    {"role": "system", "content": "You are a patient teacher."},
    {"role": "user", "content": "What is a dictionary?"},
]
conversation.append({"role": "assistant", "content": "A lookup table."})

Try it yourself

  1. 1

    Predict the order

    In the first example, work out the order of the keys in the line that prints final: before you run it. Which keys keep their place when you update a value, and where do new keys go?

  2. 2

    Count with get

    In the second example, change the word "banana" in the counting loop to your own name and run it. The line counts.get(letter, 0) + 1 is the standard counting trick: "the old count, or 0 if I haven't seen this letter".

Code & diagrams

Everyday dictionary operations New tab

Change a key name in one of the lookups to see KeyError, then use get to avoid it.

Sign in to run this example in your browser.

Expected output

apple: 30
kiwi (get): None
kiwi (get with default): 0
'apple' in prices: True
30 in prices: False
after changes: {'apple': 35, 'kiwi': 40} | removed: 50
final: {'apple': 35, 'kiwi': 45, 'grape': 60, 'plum': 20} | entries: 4
apple costs 35
kiwi costs 45
grape costs 60
plum costs 20
keys: ['apple', 'kiwi', 'grape', 'plum']
values: [35, 45, 60, 20]
A chat conversation is a list of dictionaries New tab

`dict(zip(names, numbers))` pairs up two lists item by item (`zip` is Topic 2.6) and builds a dictionary from the pairs.

Sign in to run this example in your browser.

Expected output

system -> You are a patient teacher.
user -> What is a dictionary?
assistant -> A dictionary maps keys to values.
last message: A dictionary maps keys to values.
from zip: {'a': 1, 'b': 2, 'c': 3}
letter counts: {'b': 1, 'a': 3, 'n': 2}
Merge two dictionaries with | Python 3.9+ New tab

The right-hand value wins when both dictionaries have the same key.

Sign in to run this example in your browser.

Expected output

merged: {'theme': 'light', 'size': 16, 'language': 'hi'}
defaults unchanged: {'theme': 'light', 'size': 12}
defaults after |=: {'theme': 'light', 'size': 16, 'language': 'hi'}

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Read a key that does not exist

Look up a key that was never added.

main.pywhole filepython
prices = {"apple": 30, "banana": 12}
print(prices["kiwi"])
terminal
$ python main.py
── what you'll see ──
Traceback (most recent call last):
File "main.py", line 2, in <module>
print(prices["kiwi"])
~~~~~~^^^^^^^^
KeyError: 'kiwi'

Break #2

Add keys while looping over the dictionary

Add a new entry inside a loop over the same dictionary.

main.pywhole filepython
prices = {"apple": 30, "banana": 12}
for fruit in prices:
    prices[fruit + "_sale"] = 0
terminal
$ python main.py
── what you'll see ──
Traceback (most recent call last):
File "main.py", line 2, in <module>
for fruit in prices:
^^^^^^
RuntimeError: dictionary changed size during iteration

Myth vs fact

Myth

Dictionaries have no order.

Fact

Since Python 3.7 they keep insertion order as part of the language. Two dictionaries with the same pairs in different orders are still equal (==), but they print and loop differently.

Myth

x in d checks the values.

Fact

It checks the keys. To test values use x in d.values(), which is a slow scan.

Myth

d.get(key) and d[key] are the same.

Fact

d[key] raises KeyError for a missing key; d.get(key) returns None or your default. Pick on purpose: [] for must-exist keys, get for optional ones.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    CPython's dict is a compact hash table with a separate array that stores entries in insertion order (Topic 4.5 explains the hashing). That design is why order is free, why lookup and insertion average O(1), and why deleting leaves holes that are cleaned up on the next resize.

  • ▸

    dict.get evaluates its default argument every time, even when the key exists. d.get(k, expensive()) calls expensive() on every call. Use if k in d or setdefault/defaultdict when the default is costly.

  • ▸

    Dictionary views support set operations: a.keys() & b.keys() gives the common keys and a.keys() - b.keys() the keys only in a. This is a tidy way to compare two configs.

  • ▸

    Python 3.9 added | and |= for dicts, and {**a, **b} is the older way that works everywhere. In APIs for AI models you often build a request with {**defaults, **overrides} so callers can change only what they need. All of these merges are shallow: if both sides hold a dictionary under the same key, the right-hand dictionary replaces the left-hand one completely, it is not combined ({"a": {"x": 1, "y": 2}} | {"a": {"x": 9}} is {"a": {"x": 9}}). Also, d |= pairs accepts any iterable of key-value pairs, like update, while d | other needs another dictionary.

  • ▸

    Smaller facts that interviewers like: popitem() removes and returns the last inserted pair (LIFO, since 3.7), reversed(d) works since Python 3.8, and dict.fromkeys(keys, value) shares one value object among all keys, so dict.fromkeys(names, []) gives every key the same list (use a comprehension instead). Equality ignores order, but list(a) == list(b) does not.

Remember this

  1. 1

    You write a dictionary with curly brackets and key: value pairs separated by commas: prices = {"apple": 30, "banana": 12}. prices["apple"] gives 30. An empty dictionary is {}. Keys must be unique: if you write the same key twice, the later value wins. Values can be anything, including lists and other dictionaries.

  2. 2

    To add or change an entry, assign to a key: prices["kiwi"] = 40. If the key is new, it is added; if it exists, its value is replaced. del prices["banana"] removes an entry, and prices.pop("banana") removes it and returns its value. len(prices) counts the entries.

  3. 3

    Reading a key that is not there raises KeyError. Two safer tools: prices.get("kiwi") returns None when the key is missing (or a default you give: prices.get("kiwi", 0)), and "kiwi" in prices checks the keys (not the values) and gives True or False. setdefault(key, default) returns the value if the key exists, and otherwise stores the default and returns it. update(other) copies all the pairs of another dictionary in.

  4. 4

    To go through a dictionary: for key in prices visits the keys, prices.values() the values and prices.items() the pairs, which you normally unpack: for fruit, price in prices.items():. These three are views, live windows onto the dictionary, not copies. Wrap them in list(...) when you need a snapshot.

  5. 5

    Since Python 3.7, a dictionary remembers the order in which keys were first added, and loops and printing follow that order. Updating the value of an existing key keeps its place; deleting a key and adding it again puts it at the end. Older code and other languages may treat dictionaries as unordered, but you can rely on this in modern Python.

  6. 6

    Two dictionaries can be merged into a new one with a | b (Python 3.9); when both have the same key, the right-hand value wins. a |= b updates a in place. Dictionaries are everywhere in AI work: one chat message is {"role": "user", "content": "..."}, and a whole conversation is a list of such dictionaries. That list is what a program sends to a model through its API (the doorway a program uses to ask another program for something). Dictionaries are also the shape of JSON, the text format that programs use to send data to each other; Topic 4.8 shows it.

Explain it without notes

01

How is a dictionary different from a list, and when do you use each?

02

What is the difference between d[key] and d.get(key)?

03

Does a dictionary keep order? Since which version?

04

How do you loop over the keys and values of a dictionary together?

05

What are the ways to merge two dictionaries, and what does "the right-hand value wins" mean for nested dictionaries?

Practice

01

Make a dictionary student = {"name": "Asha", "age": 21}. Add "city": "Delhi", change age to 22, then print every key and value as key: value.

02

Given marks = {"maths": 80, "science": 90}, print the mark for "english" as 0 if it is missing, without raising an error.

03

Count how many times each word appears in "to be or not to be" and print the dictionary.

Trade-offs

  • ↔

    Fast lookup costs memory. A dictionary keeps a hash table with spare room, so it uses more memory than a list of the same data. In return, finding a key does not slow down as the dictionary grows.

  • ↔

    Keys are strings, so typos are silent until you read them. user["naem"] fails only when that line runs. For data with a fixed shape, a dataclass or a Pydantic model (Topic 15.4) catches such mistakes earlier.

Done when you can

  • Done when you can create a dictionary, read, add, change and delete entries.

  • Done when you can choose between d[key], d.get(key) and key in d.

  • Done when you can loop with .items() and explain what .keys() and .values() return.

  • Done when you can say that dictionaries keep insertion order (since 3.7) and merge them with | (3.9).

  • Done when you can build a list of message dictionaries like the ones AI model APIs use.