Preparation#

Reading material#

In the Think Python (TP) book, dictionaries are covered in Chapter 10 Dictionaries, you can skip the section on memos. Tuples are covered in Chapter 11 Tuples, and we cover the first 4 sections.

Copy-and-Run#

Prep 8.1: Creating a Dictionary#

Copy and run the following code.

book = {"Title": "The Hitchhiker's Guide to the Galaxy", "Author": "Douglas Adams", "Genre": "Science fiction"}
print(book)
print(type(book))

print(book["Title"])
print(book["Author"])
print(book["Genre"])

By placing key-value pairs separated by commas inside curly braces, you created a dictionary.

Take a look at the code above and identify:

  • The curly braces that enclose the key-value pairs.

  • Commas that separate key-value pairs, how many are there?

  • Individual key-value pairs, how many are there?

  • For each key-value pair, identify the colon that separates the key from the value.

  • Identify the key of each key-value pair. What data type are they?

  • Identify the value of each key-value pair. What data type are they?

Prep 8.2: Data Types of Keys and Values#

Run the following code to create a dictionary.

city = {"name": "Mumbai", 
        "population": 21673000, 
        "gps_coordinates": ["19.07'53.2", "72.54'51.0"]}

In this example, the key-value pairs are written in separate lines. This is often done to make the code more readable.

Print the value of the keys in the dictionary, as we did in the previous example.

What data types are values in the dictionary? If you are not sure, you can use the type() function to check the data type of each value, for example print(type(city['population'])).

Look now at the code below, and try to predict what will be printed when you run it. Then run the code to check your answer.

timeline = {1982: "Started school", 
            1992: "Started high school", 
            2002: "Started university"}
print(timeline[1982])


lengths = {0.16: "Standard smartphone length",
           1.71: "Average male height",
           1.59: "Average female height",
           1.90: "Standard car height",
           2.00: "Standard door height"}

print(lengths[1.71])

What data types are the keys in the dictionaries you just tried?

As you can see, both the keys and the values in a dictionary can have different data types.

In fact, the values can be of any data type, including lists, tuples, and even other dictionaries. However, the keys must be of an immutable data type. For now, you have learned that integers, floats, strings, are immutable data types.

Look at this code. Before running it, try to predict how many key-value pairs are in the dictionary.

my_dictionary = {"first": 5, "second": 10, "first": 15, "second": 20}

print(my_dictionary)

As you can see, the dictionary keys need to be unique. If you try to create a dictionary with two identical keys, the second key-value pair will overwrite the first one. You should avoid this in your code.

Try now to predict how many key-value pairs are in the dictionary below. Then run the code to check your answer.

try_this = {"1": "first", 1: "one", 1.0: "uno"}
print(try_this)

As said, the keys in a dictionary must be unique, and this is determined by comparing the values of the keys, similar to the code below.

print("1" == 1)
print(1 == 1.0)

Prep 8.3: Modifying Dictionaries#

Run the following code and observe what gets printed.

person = {}
print(person)  
person["first_name"] = "Alice"
person["last_name"] = "Baker"
print(person)  
person["age"] = 34
person["children"] = ["Emma", "Maria", "Jacob"]
print(person)  

How many key-value pairs when the first print() function is called? What about the second and the third time?

As you can see, it is possible to add new key-value pairs to a dictionary by assigning a new key and value to the dictionary.

Try now this code.

person = {}
person["first_name"] = "Alice"
print(person)
person["first_name"] = "Josefine"
print(person)

You can see that it is possible to overwrite the value of a key in a dictionary.

Run now this code, and observe what gets printed.

person = {"first_name": "Alice", "last_name": "Baker", "age": 34}
print(person)
del person['last_name']
print(person)

You have seen how to remove a key-value pair from a dictionary by using the del keyword. Notice that the order of the remaining items is retained.

Prep 8.4: Dictionaries Are Mutable#

Since we can modify a dictionary after it is created, we say that dictionaries are mutable data types.

Earlier, you saw a few consequences of lists being mutable. One was that if two variables refer to the same list, changes made to the list through one variable will be reflected in the other variable. The second was that you could make functions that modify lists passed to them, without returning the lists. Let’s see if these consequences apply to dictionaries as well.

Run the following code to see what happens if two variables refer to the same dictionary.

popular_names = {2023: "Olivia", 2018: "Emma", 2013: "Sophia", 2010: "Isabella"}
test = popular_names
print(test)
popular_names[2007] = "Emily"
print(test)

Run the following code to see what happens if you modify a dictionary in a function.

def update_dict(dictionary, key, value):
    dictionary[key] = value

grades = {"Josefine": 12, "Jakob": 10, "Alice": 7, "Morten": 10}
print(grades)
update_dict(grades, "Alice", 12)  
print(grades)

You have seen a similar behavior with lists, so this should be less surprising.

Now, consider carefully the following code. Try to predict what will be printed when you run it. Then run the code to check your answer.

1a = {"first": 1008, "second": 2123}
2b = a
3a = {"first": 897, "second": 426}
4print(b)

The explanation to what happens here is that line 3 is reassignment, which breaks the connection between a and b.

Prep 8.5: Dictionary Items and Keyword in#

Run the code below and observe what happens.

my_dict = { "a" : 1 , "b" : 2 }
print (my_dict["c"])

Similar to IndexError for lists, if you try to retrieve the value of a key that is not in the dictionary, Python raises a KeyError.

Run now this code.

my_dict = { "a" : 100 , "b" : 200 }

print("a" in my_dict)
print("c" in my_dict)   
print(100 in my_dict)
print(200 in my_dict)

As shown by the code above, the in keyword can be used to check if a key is in a dictionary. Notice that it does not check the values, only the keys.

You can use the in keyword in combination with if statements to avoid retrieving invalid keys. Run the code below which illustrates this.

letter_frequency = {"a": 399, "e": 20, "n": 120}

for letter in "abcdefghijklmnopqrstuvwxyz":
    if letter in letter_frequency:
        print(f'{letter}:{letter_frequency[letter]}')
    else:
        print(f'{letter} not in letter_frequency')

Prep 8.6: Iterating Over Dictionaries#

You have maybe guessed that we can traverse a dictionary using a for loop. And you have just seen that we can use the in keyword to check if a key is in a dictionary.

Look at the following code. What do you think it will print? Countries, capitals, or both?

country_capitals = {"Denmark": "Copenhagen", 
            "Germany": "Berlin", 
            "France": "Paris", 
            "Sweden": "Stockholm"}

for something in country_capitals:
    print(something)

What you just saw is important to keep in mind. The for loop iterates over the keys of the dictionary.

Does this mean that we cannot iterate over the values of the dictionary? Run the following code and observe what happens.

country_capitals = {"Denmark": "Copenhagen", 
            "Germany": "Berlin", 
            "France": "Paris", 
            "Sweden": "Stockholm"}

for country in country_capitals:
    print(country_capitals[country])

Note about dictionaries. Before we move on to a another data structure, tuples, here is a small note about dictionaries. As you have seen, with dictionaries, you can give descriptive names to the data you store. However, if Python did not have dictionaries, we could achieve the similar functionality by using two lists, one for keys and one for values. You would then need to remember that elements at the same index in the two lists are related, and you would need to be careful to keep the two lists in sync. Dictionaries save you all this work, and do it in a more efficient way.

Prep 8.7: Defining Tuples#

Run this code.

point = (2, 8)
print(point)
print(point[0])
print(point[1])
print(type(point))

Now run this code.

point = 2, 8
print(point)
print(point[0])
print(point[1])
print(type(point))

Run also the next two code snippets and observe what gets printed.

sandwich = ("bacon", "lettuce", "tomato")
print(sandwich) 
print(sandwich[1])
print(type(sandwich))
something = 15, 'last name', 27.5, True
print(something)
print(something[2])
print(something[1:])
print(type(something))
print(type(something[1]))
print(type(something[1:2]))

All examples above show how to create a tuple. Similar to lists, a tuple is a collection of elements. To define a tuple, you list multiple values separated by commas. You may group the values using parentheses (), but this is not necessary. You can access the elements of a tuple using the same indexing and slicing as for lists.

When you need to create a list with only one element, you can write my_list = [1]. Let’s try to create a tuple with only one element. Run the code below and figure out how you can create a tuple with only one element.

something = (15)
print(type(something))

something = (15,)
print(type(something))

something = 15,
print(type(something))

As you can see, it is not the parentheses that define a tuple with one element, but the comma.

Try now running this code.

fruits = ("banana", "apple", "watermelon", "pineapple")
print(fruits[0])
fruits[0] = "pear"  

What you see demonstrates a big difference between lists and tuples. Lists are mutable, while tuples are immutable. You cannot change the elements of a tuple after it is created.

If you need a tuple with some other elements, you need to make a new tuple, as for example in the code below.

fruits = ("banana", "apple", "watermelon", "pineapple")
print(fruits)
fruits = ("pear",) + fruits[1:]
print(fruits)

In the reassignment above, we created a one-element tuple such that it could be concatenated with another a three-element tuple. Try checking whether you could simplify the code and write "pear" + fruits[1:] instead of ("pear",) + fruits[1:]. What about ("pear") + fruits[1:]?

Now try this code. You should be able to predict what will be printed.

fruits = ("banana", "apple", "watermelon", "pineapple")

for i in range(len(fruits)):
    print(fruits[i])

for fruit in fruits:
    print(fruit)

As you can see, traversing a tuple using a for loop can be done in the same way as for lists.

Prep 8.8: Tuple Assignment#

Run this code.

town, street, number = "Holte", "Hovedgaden", 10
print(town)
print(street)
print(number)

As you can see, it is possible to have a tuple of variable names on the left side of an assignment. Such tuple assignment can be used whenever the variable on the right side can be unpacked into the same number of variables as the tuple on the left side.

Look also at the following code, and run it.

a, b, c = [10, 100, 1000]
print(a, b, c)

Now try to run this code.

a, b, c = 19, 29, 128, 134

Try to modify the code such that there are two elements on the right side of the assignment?

As you can see, the number of tuple elements on the left, needs to match the number of elements to be unpacked on the right side of the assignment.

Prep 8.9: Functions Returning Tuples#

Look at the following code where we have a function which given a list of numbers return the minimum and maximum number. What is the type of the returned value? Run the code and observe what gets printed.

def calc_min_and_max(numbers):
    min_val = min(numbers)
    max_val = max(numbers)
    return min_val, max_val 

numbers = [28, 34, 50, 97, 76]
something = calc_min_and_max(numbers)
print(something)
print(type(something))

min_val, max_val = calc_min_and_max(numbers)
print(min_val)
print(max_val)

As you can see, the function returns a tuple. However, thanks to tuple assignment, we can unpack the tuple into two variables when calling the function. This is a very common pattern in Python. It allows you to write code which is both concise and readable.

Self quiz#

Question 8.1#

Which of these line of code will create a dictionary containing a key "a" with the value 3?

Question 8.2#

How many key-value pairs does dictionary my_dict contain after running the following code?

my_dict = {"10": 10, "message": "hello", "sequence": [1, 2, 3]}

Question 8.3#

Which of the following lines will create an empty dictionary?

Question 8.4#

What will be printed by the following piece of code?

example = {"Five": 5, 5: 8.0, "5": "message"}
print(example[5])

Question 8.5#

Given a dictionary basket={}, which of the following lines of code will correctly add a key "fruit" with value "apple"?

Question 8.6#

Assume you have a created a dictionary fruits = {"orange": 5, "banana": 10, "apple": 15} What can be used to check if "apple" is a key in the dictionary?

Question 8.7#

What data type is variable a after running the following code?

a = 18, 7

Question 8.8#

What will be the value of my_dict after running the following piece of code?

def update(d):
    d["new_key"] = 100

my_dict = {"old_key": 50}
update(my_dict)

Question 8.9#

What will be the value of data after running the following piece of code?

data = {"x": 1, "y": 2}
data["y"] = 2 * data["y"]

Question 8.10#

What will be printed when running the following piece of code?

positions = {"student": "Jakob", "supervisor": "Josefine", "professor": "Morten"}
for pos in positions:
    print(pos)

Question 8.11#

Which of these line creates a tuple?

Question 8.12#

What will be printed when running the following piece of code?

weekdays = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")
print(weekdays[1:3] + weekdays[3:4])

Question 8.13#

What will be value of my_tuple after running the following piece of code?

my_tuple = (1, 2, 3)
my_tuple[0] = 4

Question 8.14#

What will be printed when running the following piece of code?

student_data = [
    {"name": "Alice", "score": 90},
    {"name": "Bob", "score": 85},
    {"name": "Charlie", "score": 92}
]

for sd in student_data:
    for key in sd:
        print(key)

Question 8.15#

What will be printed when running the following piece of code?

student_courses = {
    "Alice": ["Math", "Physics", "Chemistry"],
    "Bob": ["Biology", "Chemistry"],
    "Charlie": ["Math", "Computer Science"]
}
print(student_courses["Charlie"][1])

Question 8.16#

How many lines is printed by the following code?

students = [("Alice", 90), ("Bob", 92), ("Charlie", 85)]
for student in students:
    name, score = students
    if score > 90:
        print(name)

Question 8.17#

What will be printed when running the following piece of code?

one, two, three = range(3)
print(two)

Question 8.18#

What will be printed when running the following piece of code?

a = {}
b = a
b["value"] = 50
a["value"] = 100
print(b)

Question 8.19#

What is the data type of the variable a after running the following code?

def my_function():
    return 13, 2.8, "hello"
a, b, c = my_function()

Question 8.20#

What will be printed when running the following function?

def stats(a, b):
    return a + b, a - b
a, b = (10, 8)
a, b = stats(a, b)
print(a, b)