9 – Dictionaries

Introduction to Python

Author

Clemens Brunner

Published

February 5, 2025

Introduction

The last built-in data type we are going to cover is the dictionary (dict). Just like its name implies, a dictionary is a mapping data type, which maps keys to values. It works a little like a real-word dictionary. Let’s assume we wanted to look up the German translation of “cat”. We’d flip through the pages of an English-German dictionary until we found the entry for “cat”. This entry would contain the German translation “Katze”. In this example, “cat” is the key and “Katze” is its value. Therefore, a dictionary is a mapping from keys to values. A dictionary can contain many key/value pairs.

Creating dictionaries

We use curly braces to create a dict, and we supply a comma-separated list of key/value pairs inside the braces (the key/value pairs are separated by colons). Here’s an example:

d = {"house": "Haus", "cat": "Katze", "snake": "Schlange"}

Alternatively, we can also use the dict function and create key/value pairs with keyword arguments:

d = dict(house="Haus", cat="Katze", snake="Schlange")

Note that in the second version, dictionary keys need to be valid Python names, whereas the first version is more flexible. For example, we can use an int as a key, but only when we use curly bracket notation:

{1: "one", 2: "two"}
{1: 'one', 2: 'two'}

The dict function raises a syntax error, because argument names must not start with a digit:

dict(1="one", 2="two")
  Cell In[4], line 1
    dict(1="one", 2="two")
         ^
SyntaxError: expression cannot contain assignment, perhaps you meant "=="?

Just like in lists and strings, Python uses square brackets to access individual elements in a dict. However, because dictionaries are not ordered, we use its keys to retrieve their corresponding values. Let’s demonstrate this with the dictionary we defined previously:

d["cat"]
'Katze'
d["house"]
'Haus'

When we use a key that does not exist in the dictionary, Python raises an error:

d["dog"]
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[7], line 1
----> 1 d["dog"]

KeyError: 'dog'

Dictionaries are mutable, which means that we can modify existing dictionary entries:

d["snake"] = "Python"
d
{'house': 'Haus', 'cat': 'Katze', 'snake': 'Python'}

We can add new entries simply by assigning a value to a new key using square bracket notation:

d["bug"] = "Wanze"
d
{'house': 'Haus', 'cat': 'Katze', 'snake': 'Python', 'bug': 'Wanze'}
Important

Again, order is irrelevant in dictionaries. There is no first, second or last item in a dictionary – values can only be accessed by their key.

So far, we have used strings as dictionary keys. However, we can actually use any immutable data type as a key, including integers, floats, and tuples. Importantly, we cannot use lists as keys, because lists are mutable. This restriction does not apply to dictionary values, which can be mutable or immutable objects.

x = {13: "A", "c": 2.22, (0, 1): [1, 2, 3]}
x
{13: 'A', 'c': 2.22, (0, 1): [1, 2, 3]}
x[[4, 5, 6]] = "X"  # try to add new entry with mutable key (list)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[11], line 1
----> 1 x[[4, 5, 6]] = "X"  # try to add new entry with mutable key (list)

TypeError: unhashable type: 'list'

The previous assignment demonstrates what happens when we try to create a dictionary entry with a list as a key: we get a TypeError (note that the error message says that the type is unhashable – for most purposes, unhashable means mutable, although technically these are different concepts).

Working with dictionaries

Not surprisingly, we can use the len function to determine the number of entries in a dictionary:

d = {"house": "Haus", "cat": "Katze", "snake": "Schlange"}
len(d)
3

Notice that an entry is a key/value pair. We can get the keys or values separately with the keys and values methods, respectively:

d.keys()
dict_keys(['house', 'cat', 'snake'])
d.values()
dict_values(['Haus', 'Katze', 'Schlange'])

These methods return list-like objects (you can basically think of them as lists).

Using the in keyword, we can check if the dictionary contains a specific key:

"cat" in d
True
"Katze" in d
False

If we want to check for a specific value, we can perform our query on d.values() instead:

"cat" in d.values()
False
"Katze" in d.values()
True

Iterating over dictionaries

Dictionaries are iterable, and if we create a loop over a dict, we actually loop over its keys:

for key in d:
    print(key)
house
cat
snake

Using the current key in each iteration, we can access the corresponding value via indexing:

for key in d:
    print(key, ":", d[key])
house : Haus
cat : Katze
snake : Schlange

Of course, we could also iterate over d.values() specifically, but often it is necessary to iterate over both keys and values simultaneously. The dict method items returns key/value pairs as tuples:

d.items()
dict_items([('house', 'Haus'), ('cat', 'Katze'), ('snake', 'Schlange')])

We can use this list-like sequence of tuples in a loop, which means that we get a tuple in each iteration. However, instead of assigning one name to the tuple, we can unpack its two items into two distinct names (this is called tuple unpacking):

for key, value in d.items():
   print(key, "=>", value)
house => Haus
cat => Katze
snake => Schlange
Tip

Here’s another example of tuple unpacking:

a, b = 12, 13
a
12
b
13

The tuple 12, 13 on the right-hand side contains two items. On the left-hand side, we assign a name to each item in the tuple, effectively unpacking the tuple into individual names. Because Python always evaluates the right-hand side first, the canonical way to swap two values in Python is very short and sweet:

a, b = b, a

This swaps the values of a and b, which we can confirm by printing their values:

a
13
b
12

Setting default values

We have already seen that accessing a non-existing dictionary entry results in a KeyError:

d = {"house": "Haus", "cat": "Katze", "snake": "Schlange"}
d["dog"]
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[28], line 2
      1 d = {"house": "Haus", "cat": "Katze", "snake": "Schlange"}
----> 2 d["dog"]

KeyError: 'dog'

There are two additional options to get values from a dictionary without raising an error. First, the get method returns a user-defined default value (by default None) if a key does not exist:

d.get("dog")  # returns None
d.get("dog", "UNDEFINED")  # returns "UNDEFINED" if the key does not exist
'UNDEFINED'
d.get("cat", "UNDEFINED")  # returns the value if the key exists
'Katze'

However, get does not automatically add new entries to the dictionary (in our example, there is still no "dog" entry in d):

d
{'house': 'Haus', 'cat': 'Katze', 'snake': 'Schlange'}

If we do want to add new key/value pairs whenever we access a non-existing key, we can use the setdefault method instead of get:

d.setdefault("dog", "UNDEFINED")
'UNDEFINED'
d
{'house': 'Haus', 'cat': 'Katze', 'snake': 'Schlange', 'dog': 'UNDEFINED'}

Exercises

  1. Create a dictionary containing three arbitrary elements. How can you access those three values individually?

  2. Add a fourth entry to the dictionary.

  3. Iterate over the dictionary and output all key/value pairs on separate lines.

  4. Access a non-existing element in the dictionary with the three different options discussed in this chapter.


https://creativecommons.org/licenses/by-nc-sa/4.0/ This document is licensed under the CC BY-NC-SA 4.0 by Clemens Brunner.