Chapter 6: Dictionaries

Source: Python Crash Course, 3rd Edition by Eric Matthes

In this chapter you’ll learn how to use Python’s dictionaries, which allow you to connect pieces of related information. You’ll learn how to access the information once it’s in a dictionary and how to modify that information. Because dictionaries can store an almost limitless amount of information, I’ll show you how to loop through the data in a dictionary. Additionally, you’ll learn to nest dictionaries inside lists, lists inside dictionaries, and even dictionaries inside other dictionaries.

Understanding dictionaries allows you to model a variety of real-world objects more accurately. You’ll be able to create a dictionary representing a person and then store as much information as you want about that person. You can store their name, age, location, profession, and any other aspect of a person you can describe. You’ll be able to store any two kinds of information that can be matched up, such as a list of words and their meanings, a list of people’s names and their favorite numbers, a list of mountains and their elevations, and so forth.

A Simple Dictionary

Consider a game featuring aliens that can have different colors and point values. This simple dictionary stores information about a particular alien:

alien.py
alien_0 = {'color': 'green', 'points': 5}

print(alien_0['color'])
print(alien_0['points'])

The dictionary alien_0 stores the alien’s color and point value. The last two lines access and display that information:

green
5

As with most new programming concepts, using dictionaries takes practice. Once you’ve worked with dictionaries for a bit, you’ll see how effectively they can model real-world situations.

Working with Dictionaries

A dictionary in Python is a collection of key-value pairs. Each key is connected to a value, and you can use a key to access the value associated with that key. A key’s value can be a number, a string, a list, or even another dictionary. In fact, you can use any object that you can create in Python as a value in a dictionary.

In Python, a dictionary is wrapped in braces ({}) with a series of key-value pairs inside the braces, as shown in the earlier example:

alien_0 = {'color': 'green', 'points': 5}

A key-value pair is a set of values associated with each other. When you provide a key, Python returns the value associated with that key. Every key is connected to its value by a colon, and individual key-value pairs are separated by commas. You can store as many key-value pairs as you want in a dictionary.

The simplest dictionary has exactly one key-value pair, as shown in this modified version of the alien_0 dictionary:

alien_0 = {'color': 'green'}

This dictionary stores one piece of information about alien_0: the alien’s color. The string 'color' is a key in this dictionary, and its associated value is 'green'.

Accessing Values in a Dictionary

To get the value associated with a key, give the name of the dictionary and then place the key inside a set of square brackets, as shown here:

alien.py
alien_0 = {'color': 'green'}
print(alien_0['color'])

This returns the value associated with the key 'color' from the dictionary alien_0:

green

You can have an unlimited number of key-value pairs in a dictionary. For example, here’s the original alien_0 dictionary with two key-value pairs:

alien_0 = {'color': 'green', 'points': 5}

Now you can access either the color or the point value of alien_0. If a player shoots down this alien, you can look up how many points they should earn using code like this:

alien_0 = {'color': 'green', 'points': 5}

new_points = alien_0['points']
print(f"You just earned {new_points} points!")

Once the dictionary has been defined, we pull the value associated with the key 'points' from the dictionary. This value is then assigned to the variable new_points. The last line prints a statement about how many points the player just earned:

You just earned 5 points!

Adding New Key-Value Pairs

Dictionaries are dynamic structures, and you can add new key-value pairs to a dictionary at any time. To add a new key-value pair, you would give the name of the dictionary followed by the new key in square brackets, along with the new value.

Let’s add two new pieces of information to the alien_0 dictionary: the alien’s x- and y-coordinates, which will help us display the alien at a particular position on the screen. Let’s place the alien on the left edge of the screen, 25 pixels down from the top:

alien.py
alien_0 = {'color': 'green', 'points': 5}
print(alien_0)

alien_0['x_position'] = 0
alien_0['y_position'] = 25
print(alien_0)

We start by defining the same dictionary that we’ve been working with. We then print this dictionary, displaying a snapshot of its information. Next, we add a new key-value pair to the dictionary: the key 'x_position' and the value 0. We do the same for the key 'y_position'. When we print the modified dictionary, we see the two additional key-value pairs:

{'color': 'green', 'points': 5}
{'color': 'green', 'points': 5, 'x_position': 0, 'y_position': 25}

The final version of the dictionary contains four key-value pairs. The original two specify color and point value, and two more specify the alien’s position.

Dictionaries retain the order in which they were defined. When you print a dictionary or loop through its elements, you will see the elements in the same order they were added to the dictionary.

Starting with an Empty Dictionary

It’s sometimes convenient, or even necessary, to start with an empty dictionary and then add each new item to it. To start filling an empty dictionary, define a dictionary with an empty set of braces and then add each key-value pair on its own line. For example, here’s how to build the alien_0 dictionary using this approach:

alien.py
alien_0 = {}

alien_0['color'] = 'green'
alien_0['points'] = 5

print(alien_0)

We first define an empty alien_0 dictionary, and then add color and point values to it. The result is the dictionary we’ve been using in previous examples:

{'color': 'green', 'points': 5}

Typically, you’ll use empty dictionaries when storing user-supplied data in a dictionary or when writing code that generates a large number of key-value pairs automatically.

Modifying Values in a Dictionary

To modify a value in a dictionary, give the name of the dictionary with the key in square brackets and then the new value you want associated with that key. For example, consider an alien that changes from green to yellow as a game progresses:

alien.py
alien_0 = {'color': 'green'}
print(f"The alien is {alien_0['color']}.")

alien_0['color'] = 'yellow'
print(f"The alien is now {alien_0['color']}.")

We first define a dictionary for alien_0 that contains only the alien’s color; then we change the value associated with the key 'color' to 'yellow'. The output shows that the alien has indeed changed from green to yellow:

The alien is green.
The alien is now yellow.

For a more interesting example, let’s track the position of an alien that can move at different speeds. We’ll store a value representing the alien’s current speed and then use it to determine how far to the right the alien should move:

alien_0 = {'x_position': 0, 'y_position': 25, 'speed': 'medium'}
print(f"Original position: {alien_0['x_position']}")

# Move the alien to the right.
# Determine how far to move the alien based on its current speed.
if alien_0['speed'] == 'slow':          (1)
    x_increment = 1
elif alien_0['speed'] == 'medium':
    x_increment = 2
else:
    # This must be a fast alien.
    x_increment = 3

# The new position is the old position plus the increment.
alien_0['x_position'] = alien_0['x_position'] + x_increment  (2)

print(f"New position: {alien_0['x_position']}")
1 An if-elif-else chain determines how far the alien should move to the right and assigns this value to the variable x_increment.
2 The increment is added to the value of x_position, and the result is stored back in the dictionary.

Because this is a medium-speed alien, its position shifts two units to the right:

Original position: 0
New position: 2

By changing one value in the alien’s dictionary, you can change the overall behavior of the alien. For example, to turn this medium-speed alien into a fast alien, you would add this line:

alien_0['speed'] = 'fast'

The if-elif-else block would then assign a larger value to x_increment the next time the code runs.

Removing Key-Value Pairs

When you no longer need a piece of information that’s stored in a dictionary, you can use the del statement to completely remove a key-value pair. All del needs is the name of the dictionary and the key that you want to remove.

For example, let’s remove the key 'points' from the alien_0 dictionary, along with its value:

alien.py
alien_0 = {'color': 'green', 'points': 5}
print(alien_0)

del alien_0['points']  (1)
print(alien_0)
1 The del statement tells Python to delete the key 'points' from the dictionary alien_0 and to remove the value associated with that key as well.
{'color': 'green', 'points': 5}
{'color': 'green'}

Be aware that the deleted key-value pair is removed permanently.

A Dictionary of Similar Objects

The previous example involved storing different kinds of information about one object, an alien in a game. You can also use a dictionary to store one kind of information about many objects. For example, say you want to poll a number of people and ask them what their favorite programming language is. A dictionary is useful for storing the results of a simple poll, like this:

favorite_languages.py
favorite_languages = {
    'jen': 'python',
    'sarah': 'c',
    'edward': 'rust',
    'phil': 'python',
    }

As you can see, we’ve broken a larger dictionary into several lines. Each key is the name of a person who responded to the poll, and each value is their language choice. When you know you’ll need more than one line to define a dictionary, press Enter after the opening brace. Then indent the next line one level (four spaces) and write the first key-value pair, followed by a comma. From this point forward when you press Enter, your text editor should automatically indent all subsequent key-value pairs to match the first key-value pair.

Once you’ve finished defining the dictionary, add a closing brace on a new line after the last key-value pair, and indent it one level so it aligns with the keys in the dictionary. It’s good practice to include a comma after the last key-value pair as well, so you’re ready to add a new key-value pair on the next line.

Most editors have some functionality that helps you format extended lists and dictionaries in a similar manner to this example. Other acceptable ways to format long dictionaries are available as well, so you may see slightly different formatting in your editor, or in other sources.

To use this dictionary, given the name of a person who took the poll, you can easily look up their favorite language:

favorite_languages.py
favorite_languages = {
    'jen': 'python',
    'sarah': 'c',
    'edward': 'rust',
    'phil': 'python',
    }

language = favorite_languages['sarah'].title()  (1)
print(f"Sarah's favorite language is {language}.")
1 We ask for the value at favorite_languages['sarah'] and assign it to the variable language. Creating a new variable here makes for a much cleaner print() call.
Sarah's favorite language is C.

You could use this same syntax with any individual represented in the dictionary.

Using get() to Access Values

Using keys in square brackets to retrieve the value you’re interested in from a dictionary might cause one potential problem: if the key you ask for doesn’t exist, you’ll get an error.

Let’s see what happens when you ask for the point value of an alien that doesn’t have a point value set:

alien_no_points.py
alien_0 = {'color': 'green', 'speed': 'slow'}
print(alien_0['points'])

This results in a traceback, showing a KeyError:

Traceback (most recent call last):
  File "alien_no_points.py", line 2, in <module>
    print(alien_0['points'])
          ~~~~~~~^^^^^^^^^^
KeyError: 'points'

You’ll learn more about how to handle errors like this in general in Chapter 10. For dictionaries specifically, you can use the get() method to set a default value that will be returned if the requested key doesn’t exist.

The get() method requires a key as a first argument. As a second optional argument, you can pass the value to be returned if the key doesn’t exist:

alien_0 = {'color': 'green', 'speed': 'slow'}

point_value = alien_0.get('points', 'No point value assigned.')
print(point_value)

If the key 'points' exists in the dictionary, you’ll get the corresponding value. If it doesn’t, you get the default value. In this case, points doesn’t exist, and we get a clean message instead of an error:

No point value assigned.

If there’s a chance the key you’re asking for might not exist, consider using the get() method instead of the square bracket notation.

If you leave out the second argument in the call to get() and the key doesn’t exist, Python will return the value None. The special value None means "no value exists." This is not an error: it’s a special value meant to indicate the absence of a value. You’ll see more uses for None in Chapter 8.

Try It Yourself

6-1. Person: Use a dictionary to store information about a person you know. Store their first name, last name, age, and the city in which they live. You should have keys such as first_name, last_name, age, and city. Print each piece of information stored in your dictionary.

6-2. Favorite Numbers: Use a dictionary to store people’s favorite numbers. Think of five names, and use them as keys in your dictionary. Think of a favorite number for each person, and store each as a value in your dictionary. Print each person’s name and their favorite number. For even more fun, poll a few friends and get some actual data for your program.

6-3. Glossary: A Python dictionary can be used to model an actual dictionary. However, to avoid confusion, let’s call it a glossary. Think of five programming words you’ve learned about in the previous chapters. Use these words as the keys in your glossary, and store their meanings as values. Print each word and its meaning as neatly formatted output. You might print the word followed by a colon and then its meaning, or print the word on one line and then print its meaning indented on a second line. Use the newline character (\n) to insert a blank line between each word-meaning pair in your output.

Looping Through a Dictionary

A single Python dictionary can contain just a few key-value pairs or millions of pairs. Because a dictionary can contain large amounts of data, Python lets you loop through a dictionary. Dictionaries can be used to store information in a variety of ways; therefore, several different ways exist to loop through them. You can loop through all of a dictionary’s key-value pairs, through its keys, or through its values.

Looping Through All Key-Value Pairs

Before we explore the different approaches to looping, let’s consider a new dictionary designed to store information about a user on a website. The following dictionary would store one person’s username, first name, and last name:

user.py
user_0 = {
    'username': 'efermi',
    'first': 'enrico',
    'last': 'fermi',
    }

You can access any single piece of information about user_0 based on what you’ve already learned in this chapter. But what if you wanted to see everything stored in this user’s dictionary? To do so, you could loop through the dictionary using a for loop:

user_0 = {
    'username': 'efermi',
    'first': 'enrico',
    'last': 'fermi',
    }

for key, value in user_0.items():
    print(f"\nKey: {key}")
    print(f"Value: {value}")

To write a for loop for a dictionary, you create names for the two variables that will hold the key and value in each key-value pair. You can choose any names you want for these two variables. This code would work just as well if you had used abbreviations for the variable names, like this:

for k, v in user_0.items()

The second half of the for statement includes the name of the dictionary followed by the method items(), which returns a sequence of key-value pairs. The for loop then assigns each of these pairs to the two variables provided. In the preceding example, we use the variables to print each key, followed by the associated value. The "\n" in the first print() call ensures that a blank line is inserted before each key-value pair in the output:

Key: username
Value: efermi

Key: first
Value: enrico

Key: last
Value: fermi

Looping through all key-value pairs works particularly well for dictionaries like favorite_languages.py, which stores the same kind of information for many different keys. If you loop through the favorite_languages dictionary, you get the name of each person in the dictionary and their favorite programming language. Because the keys always refer to a person’s name and the value is always a language, we’ll use the variables name and language in the loop instead of key and value. This will make it easier to follow what’s happening inside the loop:

favorite_languages.py
favorite_languages = {
    'jen': 'python',
    'sarah': 'c',
    'edward': 'rust',
    'phil': 'python',
    }

for name, language in favorite_languages.items():
    print(f"{name.title()}'s favorite language is {language.title()}.")

This code tells Python to loop through each key-value pair in the dictionary. As it works through each pair the key is assigned to the variable name, and the value is assigned to the variable language. These descriptive names make it much easier to see what the print() call is doing.

Now, in just a few lines of code, we can display all of the information from the poll:

Jen's favorite language is Python.
Sarah's favorite language is C.
Edward's favorite language is Rust.
Phil's favorite language is Python.

This type of looping would work just as well if our dictionary stored the results from polling a thousand or even a million people.

Looping Through All the Keys in a Dictionary

The keys() method is useful when you don’t need to work with all of the values in a dictionary. Let’s loop through the favorite_languages dictionary and print the names of everyone who took the poll:

favorite_languages = {
    'jen': 'python',
    'sarah': 'c',
    'edward': 'rust',
    'phil': 'python',
    }

for name in favorite_languages.keys():
    print(name.title())

This for loop tells Python to pull all the keys from the dictionary favorite_languages and assign them one at a time to the variable name. The output shows the names of everyone who took the poll:

Jen
Sarah
Edward
Phil

Looping through the keys is actually the default behavior when looping through a dictionary, so this code would have exactly the same output if you wrote:

for name in favorite_languages:

rather than:

for name in favorite_languages.keys():

You can choose to use the keys() method explicitly if it makes your code easier to read, or you can omit it if you wish.

You can access the value associated with any key you care about inside the loop, by using the current key. Let’s print a message to a couple of friends about the languages they chose. We’ll loop through the names in the dictionary as we did previously, but when the name matches one of our friends, we’ll display a message about their favorite language:

favorite_languages = {
    # --snip--
    }

friends = ['phil', 'sarah']
for name in favorite_languages.keys():
    print(f"Hi {name.title()}.")

    if name in friends:                                        (1)
        language = favorite_languages[name].title()           (2)
        print(f"\t{name.title()}, I see you love {language}!")
1 Inside the loop, we check whether the name we’re working with is in the list friends.
2 If it is, we determine the person’s favorite language using the name of the dictionary and the current value of name as the key.

Everyone’s name is printed, but our friends receive a special message:

Hi Jen.
Hi Sarah.
	Sarah, I see you love C!
Hi Edward.
Hi Phil.
	Phil, I see you love Python!

You can also use the keys() method to find out if a particular person was polled. This time, let’s find out if Erin took the poll:

favorite_languages = {
    # --snip--
    }

if 'erin' not in favorite_languages.keys():
    print("Erin, please take our poll!")

The keys() method isn’t just for looping: it actually returns a sequence of all the keys, and the if statement simply checks if 'erin' is in this sequence. Because she’s not, a message is printed inviting her to take the poll:

Erin, please take our poll!

Looping Through a Dictionary’s Keys in a Particular Order

Looping through a dictionary returns the items in the same order they were inserted. Sometimes, though, you’ll want to loop through a dictionary in a different order.

One way to do this is to sort the keys as they’re returned in the for loop. You can use the sorted() function to get a copy of the keys in order:

favorite_languages = {
    'jen': 'python',
    'sarah': 'c',
    'edward': 'rust',
    'phil': 'python',
    }

for name in sorted(favorite_languages.keys()):
    print(f"{name.title()}, thank you for taking the poll.")

This for statement is like other for statements, except that we’ve wrapped the sorted() function around the dictionary.keys() method. This tells Python to get all the keys in the dictionary and sort them before starting the loop. The output shows everyone who took the poll, with the names displayed in order:

Edward, thank you for taking the poll.
Jen, thank you for taking the poll.
Phil, thank you for taking the poll.
Sarah, thank you for taking the poll.

Looping Through All Values in a Dictionary

If you are primarily interested in the values that a dictionary contains, you can use the values() method to return a sequence of values without any keys. For example, say we simply want a list of all languages chosen in our programming language poll, without the name of the person who chose each language:

favorite_languages = {
    'jen': 'python',
    'sarah': 'c',
    'edward': 'rust',
    'phil': 'python',
    }

print("The following languages have been mentioned:")
for language in favorite_languages.values():
    print(language.title())

The for statement here pulls each value from the dictionary and assigns it to the variable language. When these values are printed, we get a list of all chosen languages:

The following languages have been mentioned:
Python
C
Rust
Python

This approach pulls all the values from the dictionary without checking for repeats. This might work fine with a small number of values, but in a poll with a large number of respondents, it would result in a very repetitive list. To see each language chosen without repetition, we can use a set. A set is a collection in which each item must be unique:

favorite_languages = {
    # --snip--
    }

print("The following languages have been mentioned:")
for language in set(favorite_languages.values()):
    print(language.title())

When you wrap set() around a collection of values that contains duplicate items, Python identifies the unique items in the collection and builds a set from those items. Here we use set() to pull out the unique languages in favorite_languages.values().

The result is a nonrepetitive list of languages that have been mentioned by people taking the poll:

The following languages have been mentioned:
Python
C
Rust

You can build a set directly using braces and separating the elements with commas:

>>> languages = {'python', 'rust', 'python', 'c'}
>>> languages
{'rust', 'python', 'c'}

It’s easy to mistake sets for dictionaries because they’re both wrapped in braces. When you see braces but no key-value pairs, you’re probably looking at a set. Unlike lists and dictionaries, sets do not retain items in any specific order.

Try It Yourself

6-4. Glossary 2: Now that you know how to loop through a dictionary, clean up the code from Exercise 6-3 by replacing your series of print() calls with a loop that runs through the dictionary’s keys and values. When you’re sure that your loop works, add five more Python terms to your glossary. When you run your program again, these new words and meanings should automatically be included in the output.

6-5. Rivers: Make a dictionary containing three major rivers and the country each river runs through. One key-value pair might be 'nile': 'egypt'. Use a loop to print a sentence about each river, such as The Nile runs through Egypt. Use a loop to print the name of each river included in the dictionary. Use a loop to print the name of each country included in the dictionary.

6-6. Polling: Use the code in favorite_languages.py. Make a list of people who should take the favorite languages poll. Include some names that are already in the dictionary and some that are not. Loop through the list of people who should take the poll. If they have already taken the poll, print a message thanking them for responding. If they have not yet taken the poll, print a message inviting them to take the poll.

Nesting

Sometimes you’ll want to store multiple dictionaries in a list, or a list of items as a value in a dictionary. This is called nesting. You can nest dictionaries inside a list, a list of items inside a dictionary, or even a dictionary inside another dictionary. Nesting is a powerful feature, as the following examples will demonstrate.

A List of Dictionaries

The alien_0 dictionary contains a variety of information about one alien, but it has no room to store information about a second alien, much less a screen full of aliens. How can you manage a fleet of aliens? One way is to make a list of aliens in which each alien is a dictionary of information about that alien. For example, the following code builds a list of three aliens:

aliens.py
alien_0 = {'color': 'green', 'points': 5}
alien_1 = {'color': 'yellow', 'points': 10}
alien_2 = {'color': 'red', 'points': 15}

aliens = [alien_0, alien_1, alien_2]  (1)

for alien in aliens:
    print(alien)
1 We store each of the three dictionaries in a list called aliens.
{'color': 'green', 'points': 5}
{'color': 'yellow', 'points': 10}
{'color': 'red', 'points': 15}

A more realistic example would involve more than three aliens with code that automatically generates each alien. In the following example, we use range() to create a fleet of 30 aliens:

# Make an empty list for storing aliens.
aliens = []

# Make 30 green aliens.
for alien_number in range(30):           (1)
    new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'}  (2)
    aliens.append(new_alien)             (3)

# Show the first 5 aliens.
for alien in aliens[:5]:                 (4)
    print(alien)
print("...")

# Show how many aliens have been created.
print(f"Total number of aliens: {len(aliens)}")
1 The range() function returns a series of numbers that tells Python how many times we want the loop to repeat.
2 Each time the loop runs, we create a new alien.
3 Each new alien is appended to the list aliens.
4 We use a slice to print the first five aliens.
{'color': 'green', 'points': 5, 'speed': 'slow'}
{'color': 'green', 'points': 5, 'speed': 'slow'}
{'color': 'green', 'points': 5, 'speed': 'slow'}
{'color': 'green', 'points': 5, 'speed': 'slow'}
{'color': 'green', 'points': 5, 'speed': 'slow'}
...

Total number of aliens: 30

These aliens all have the same characteristics, but Python considers each one a separate object, which allows us to modify each alien individually.

How might you work with a group of aliens like this? Imagine that one aspect of a game has some aliens changing color and moving faster as the game progresses. When it’s time to change colors, we can use a for loop and an if statement to change the color of the aliens. For example, to change the first three aliens to yellow, medium-speed aliens worth 10 points each, we could do this:

# Make an empty list for storing aliens.
aliens = []

# Make 30 green aliens.
for alien_number in range(30):
    new_alien = {'color': 'green', 'points': 5, 'speed': 'slow'}
    aliens.append(new_alien)

for alien in aliens[:3]:
    if alien['color'] == 'green':
        alien['color'] = 'yellow'
        alien['speed'] = 'medium'
        alien['points'] = 10

# Show the first 5 aliens.
for alien in aliens[:5]:
    print(alien)
print("...")

Because we want to modify the first three aliens, we loop through a slice that includes only the first three aliens. All of the aliens are green now, but that won’t always be the case, so we write an if statement to make sure we’re only modifying green aliens. If the alien is green, we change the color to 'yellow', the speed to 'medium', and the point value to 10:

{'color': 'yellow', 'points': 10, 'speed': 'medium'}
{'color': 'yellow', 'points': 10, 'speed': 'medium'}
{'color': 'yellow', 'points': 10, 'speed': 'medium'}
{'color': 'green', 'points': 5, 'speed': 'slow'}
{'color': 'green', 'points': 5, 'speed': 'slow'}
...

You could expand this loop by adding an elif block that turns yellow aliens into red, fast-moving ones worth 15 points each. Without showing the entire program again, that loop would look like this:

for alien in aliens[0:3]:
    if alien['color'] == 'green':
        alien['color'] = 'yellow'
        alien['speed'] = 'medium'
        alien['points'] = 10
    elif alien['color'] == 'yellow':
        alien['color'] = 'red'
        alien['speed'] = 'fast'
        alien['points'] = 15

It’s common to store a number of dictionaries in a list when each dictionary contains many kinds of information about one object. For example, you might create a dictionary for each user on a website, and store the individual dictionaries in a list called users. All of the dictionaries in the list should have an identical structure, so you can loop through the list and work with each dictionary object in the same way.

A List in a Dictionary

Rather than putting a dictionary inside a list, it’s sometimes useful to put a list inside a dictionary. For example, consider how you might describe a pizza that someone is ordering. If you were to use only a list, all you could really store is a list of the pizza’s toppings. With a dictionary, a list of toppings can be just one aspect of the pizza you’re describing.

In the following example, two kinds of information are stored for each pizza: a type of crust and a list of toppings. The list of toppings is a value associated with the key 'toppings'. To use the items in the list, we give the name of the dictionary and the key 'toppings', as we would any value in the dictionary. Instead of returning a single value, we get a list of toppings:

pizza.py
# Store information about a pizza being ordered.
pizza = {
    'crust': 'thick',
    'toppings': ['mushrooms', 'extra cheese'],
    }

# Summarize the order.
print(f"You ordered a {pizza['crust']}-crust pizza "   (1)
      "with the following toppings:")

for topping in pizza['toppings']:                       (2)
    print(f"\t{topping}")
1 We summarize the order before building the pizza. When you need to break up a long line in a print() call, Python will automatically combine all of the strings it finds inside the parentheses.
2 To print the toppings, we write a for loop. To access the list of toppings, we use the key 'toppings', and Python grabs the list of toppings from the dictionary.
You ordered a thick-crust pizza with the following toppings:
	mushrooms
	extra cheese

You can nest a list inside a dictionary anytime you want more than one value to be associated with a single key in a dictionary. In the earlier example of favorite programming languages, if we were to store each person’s responses in a list, people could choose more than one favorite language. When we loop through the dictionary, the value associated with each person would be a list of languages rather than a single language. Inside the dictionary’s for loop, we use another for loop to run through the list of languages associated with each person:

favorite_languages.py
favorite_languages = {
    'jen': ['python', 'rust'],
    'sarah': ['c'],
    'edward': ['rust', 'go'],
    'phil': ['python', 'haskell'],
    }

for name, languages in favorite_languages.items():       (1)
    print(f"\n{name.title()}'s favorite languages are:")
    for language in languages:                           (2)
        print(f"\t{language.title()}")
1 When we loop through the dictionary, we use the variable name languages to hold each value from the dictionary, because we know that each value will be a list.
2 Inside the main dictionary loop, we use another for loop to run through each person’s list of favorite languages.

Now each person can list as many favorite languages as they like:

Jen's favorite languages are:
	Python
	Rust

Sarah's favorite languages are:
	C

Edward's favorite languages are:
	Rust
	Go

Phil's favorite languages are:
	Python
	Haskell

To refine this program even further, you could include an if statement at the beginning of the dictionary’s for loop to see whether each person has more than one favorite language by examining the value of len(languages). If a person has more than one favorite, the output would stay the same. If the person has only one favorite language, you could change the wording to reflect that. For example, you could say, "Sarah’s favorite language is C."

You should not nest lists and dictionaries too deeply. If you’re nesting items much deeper than what you see in the preceding examples, or if you’re working with someone else’s code with significant levels of nesting, there’s most likely a simpler way to solve the problem.

A Dictionary in a Dictionary

You can nest a dictionary inside another dictionary, but your code can get complicated quickly when you do. For example, if you have several users for a website, each with a unique username, you can use the usernames as the keys in a dictionary. You can then store information about each user by using a dictionary as the value associated with their username. In the following listing, we store three pieces of information about each user: their first name, last name, and location:

many_users.py
users = {
    'aeinstein': {
        'first': 'albert',
        'last': 'einstein',
        'location': 'princeton',
        },
    'mcurie': {
        'first': 'marie',
        'last': 'curie',
        'location': 'paris',
        },
    }

for username, user_info in users.items():              (1)
    print(f"\nUsername: {username}")                   (2)
    full_name = f"{user_info['first']} {user_info['last']}"  (3)
    location = user_info['location']

    print(f"\tFull name: {full_name.title()}")         (4)
    print(f"\tLocation: {location.title()}")
1 We loop through the users dictionary. Python assigns each key to the variable username, and the dictionary associated with each username is assigned to the variable user_info.
2 Once inside the main dictionary loop, we print the username.
3 We start accessing the inner dictionary. The variable user_info has three keys: 'first', 'last', and 'location'. We use each key to generate a neatly formatted full name and location.
4 We print a summary of what we know about each user.
Username: aeinstein
	Full name: Albert Einstein
	Location: Princeton

Username: mcurie
	Full name: Marie Curie
	Location: Paris

Notice that the structure of each user’s dictionary is identical. Although not required by Python, this structure makes nested dictionaries easier to work with. If each user’s dictionary had different keys, the code inside the for loop would be more complicated.

Try It Yourself

6-7. People: Start with the program you wrote for Exercise 6-1. Make two new dictionaries representing different people, and store all three dictionaries in a list called people. Loop through your list of people. As you loop through the list, print everything you know about each person.

6-8. Pets: Make several dictionaries, where each dictionary represents a different pet. In each dictionary, include the kind of animal and the owner’s name. Store these dictionaries in a list called pets. Next, loop through your list and as you do, print everything you know about each pet.

6-9. Favorite Places: Make a dictionary called favorite_places. Think of three names to use as keys in the dictionary, and store one to three favorite places for each person. To make this exercise a bit more interesting, ask some friends to name a few of their favorite places. Loop through the dictionary, and print each person’s name and their favorite places.

6-10. Favorite Numbers: Modify your program from Exercise 6-2 so each person can have more than one favorite number. Then print each person’s name along with their favorite numbers.

6-11. Cities: Make a dictionary called cities. Use the names of three cities as keys in your dictionary. Create a dictionary of information about each city and include the country that the city is in, its approximate population, and one fact about that city. The keys for each city’s dictionary should be something like country, population, and fact. Print the name of each city and all of the information you have stored about it.

6-12. Extensions: We’re now working with examples that are complex enough that they can be extended in any number of ways. Use one of the example programs from this chapter, and extend it by adding new keys and values, changing the context of the program, or improving the formatting of the output.

Summary

In this chapter, you learned how to define a dictionary and how to work with the information stored in a dictionary. You learned how to access and modify individual elements in a dictionary, and how to loop through all of the information in a dictionary. You learned to loop through a dictionary’s key-value pairs, its keys, and its values. You also learned how to nest multiple dictionaries in a list, nest lists in a dictionary, and nest a dictionary inside a dictionary.

In the next chapter you’ll learn about while loops and how to accept input from people who are using your programs. This will be an exciting chapter, because you’ll learn to make all of your programs interactive: they’ll be able to respond to user input.

Applied Exercises: Ch 6 — Dictionaries

These exercises cover the same concepts as the chapter but use context from real infrastructure, network security, and language learning work. Save each as a separate .py file using lowercase and underscores, e.g. ise_node_info.py.

Domus Digitalis / Homelab

D6-1. Node Profile: Create a dictionary called node representing one of your Domus Digitalis hosts (e.g., kvm-01). Include keys for hostname, os, role, ip, and vlan. Print each key-value pair with a label.

D6-2. VLAN Registry: Create a dictionary called vlan_registry where keys are VLAN IDs (as strings or integers) and values are VLAN names (e.g., 100: 'INFRA'). Use items() to loop through and print each VLAN ID and name. Then use sorted() to print them in numeric order.

D6-3. Stack Info: Create a dictionary called stack where each key is a service name and each value is a dictionary containing host, port, and status. Include at least four services (e.g., Vault, FreeIPA, Wazuh, Gitea). Loop through the stack and print a formatted summary for each service.

D6-4. BGP Peers: Create a dictionary called bgp_peers where each key is a peer name (e.g., 'ifog', 'route64') and each value is a list containing the peer’s ASN and tunnel IP. Loop through the dictionary and print a summary for each peer. Use get() to safely retrieve an optional 'description' key that may not exist on all peers.

D6-5. Fleet Inventory: Create an empty list called fleet. Use a loop and range() to generate five node dictionaries, each with keys id, hostname, and status (all initially 'up'). Append each to the fleet list. Then loop through the first three nodes and change their status to 'maintenance'. Print all five nodes.

CHLA / ISE / Network Security

C6-1. ISE Node Profile: Create a dictionary called ise_node representing your production ISE node. Include keys for hostname, role, version, persona, and ip. Print each piece of information with a label.

C6-2. Policy Set Map: Create a dictionary called policy_sets where each key is a policy set name and each value is a dictionary containing auth_protocol, condition, and result. Include at least three policy sets. Loop through the dictionary using items() and print a formatted summary for each.

C6-3. Syslog Severity Labels: Create a dictionary called syslog_levels where keys are integers 0–7 and values are the syslog severity label strings (Emergency through Debug). Loop through the dictionary in sorted key order and print each level and label. Use get() to safely look up a level that may not exist and print a default message if it doesn’t.

C6-4. Pipeline Stages: Create a dictionary called pipeline where each key is a stage name and each value is a list of log types processed at that stage. Include at least three stages (e.g., 'ingest', 'enrich', 'route'). Loop through the pipeline and for each stage, print the stage name followed by each log type on its own indented line.

C6-5. Endpoint Group Registry: Create a dictionary called endpoints where each key is an endpoint group name and each value is a dictionary containing vlan, acl, and description. Add at least three groups. Loop through the dictionary and print a formatted summary for each group. Then add a new group dynamically and confirm it appears in the output.

General Sysadmin / Linux

L6-1. Service Profile: Create a dictionary called service representing one systemd service you manage. Include keys for name, state, enabled, pid, and description. Print each piece of information with a label.

L6-2. Package Versions: Create a dictionary called packages where keys are package names and values are installed version strings. Use sorted() to loop through and print each package and version in alphabetical order. Use get() to safely check for a package that may not be in the dictionary.

L6-3. Mount Points: Create a dictionary called mounts where each key is a mount point path and each value is a dictionary containing device, fstype, and usage_percent. Include at least four mount points. Loop through the dictionary and print a formatted summary. Flag any mount point with usage_percent above 80.

L6-4. Cron Job Registry: Create a dictionary called cron_jobs where each key is a job name and each value is a list containing the schedule string and the command. Loop through the dictionary using items() and print each job name, schedule, and command on labeled lines.

L6-5. User Roles: Create a dictionary called user_roles where keys are usernames and values are lists of roles assigned to that user. Include at least four users. Loop through the dictionary and for each user, print their username followed by each role on its own indented line. Check whether a specific user has the 'sudo' role using in.

Spanish / DELE C2

E6-1. Vocabulary Dictionary: Create a dictionary called vocabulario where keys are Spanish words from your Don Quijote reading and values are their English definitions. Include at least six words. Print each word and definition in a formatted way, with a blank line between entries.

E6-2. Chapter Notes: Create a dictionary called notas_capitulos where keys are chapter numbers (integers) and values are dictionaries containing tema (main theme), personajes (list of characters mentioned), and vocabulario_nuevo (list of new words). Include at least three chapters. Loop through and print a formatted summary of each chapter’s notes.

E6-3. Grammar Topics: Create a dictionary called gramatica where keys are grammar topic names (e.g., 'subjuntivo', 'perífrasis') and values are lists of example sentences. Loop through the dictionary and for each topic, print the topic name followed by each example on its own indented line.

E6-4. DELE Study Tracker: Create a dictionary called progreso where keys are DELE skill areas (e.g., 'comprensión lectora', 'expresión escrita') and values are dictionaries containing completado (boolean), puntuación (score out of 100), and notas (a string). Loop through and print a formatted summary. Use get() to safely retrieve an optional 'siguiente_paso' key that may not exist on all entries.

E6-5. Author Profiles: Create a dictionary called autores where keys are author names and values are dictionaries containing época, obras_principales (a list), and estilo. Include Cervantes and at least two other Spanish-language authors. Loop through and print a formatted profile for each author, including each main work on its own indented line.