Preparation#
A note about the reading material and preparation exercises. The reading material and preparation exercises both cover the same programming concepts but with slightly different approaches. The reading material aims to explain the concepts, while we designed the preparation exercises for you to try things out. You can decide which to start with. Some prefer reading the chapters from the book first to feel more prepared for the exercises, while others may dive into the exercises and use the book to clarify things as needed. If you’re finding programming challenging, you might benefit from reading the chapters first, then doing the exercises, and returning to the book for clarification.
Reading material#
In the Think Python (TP) book, lists are covered in Chapter 9 Lists.
Copy-and-Run#
Prep 6.1: Creating Lists#
Run each of the following code snippets.
odd_digits = [1, 3, 5, 7, 9]
print(odd_digits)
even_digits = [0, 2, 4, 6, 8]
print(even_digits)
common_allergies = ['peanuts', 'shellfish', 'dairy', 'eggs', 'gluten']
print(common_allergies)
noble_gasses = ['helium', 'neon', 'argon', 'krypton', 'xenon', 'radon']
print(noble_gasses)
By placing items separated by commas inside square brackets, you created a list. Lists allow you to store multiple items under the same variable name.
Look at the following attempts at creating lists. Do you think they will run? Try it out!
mixed_list = [1, 'hello', 3.14, True]
print(mixed_list)
numpad_buttons = [[7, 8, 9], [4, 5, 6], [1, 2, 3], [0]]
print(numpad_buttons)
i_am_empty_inside = []
print(i_am_empty_inside)
Prep 6.2: List Indexing#
Now, try running the following code.
list_of_numbers = [10, 20, 30, 40, 50]
print("item at index 0:", list_of_numbers[0])
print("item at index 3:", list_of_numbers[3])
You access list elements by using square brackets []
after the list variable name. The number inside the square brackets is the index of the element you want to access. Python is a zero-indexed language, so the first element of a list has an index of 0, the second element has an index of 1, and so on.
Now, try this code.
noble_gasses = ['helium', 'neon', 'argon', 'krypton', 'xenon', 'radon']
print("item at index 1:", noble_gasses[1])
print("item at index -1:", noble_gasses[-1])
print("item at index -2:", noble_gasses[-2])
print("item at index -3:", noble_gasses[-3])
A negative index can be used to access elements from the end of the list!
The figure below shows how the elements of a list are indexed.
Prep 6.3: List Slicing#
Run now the following code.
my_list = ["Zero", "One", "Two", "Three", "Four",
"Five", "Six", "Seven", "Eight", "Nine"]
print(my_list[2:5])
print(my_list[3:8:2])
print(my_list[0:10:3])
Slicing accesses a range of elements in a list. The syntax for slicing is list[start:stop:step]
, which is similar to range
. And like with range
, you can omit the default values. It is a good idea to experiment with slicing to understand how it works. Run the following code and experiment with different values.
my_list = ["Zero", "One", "Two", "Three", "Four",
"Five", "Six", "Seven", "Eight", "Nine"]
print(my_list[::2])
print(my_list[:-1])
print(my_list[::-1])
print(my_list[3:])
print(my_list[:2])
Now, see yet another use of slicing.
my_list = ["Zero", "One", "Two", "Three", "Four",
"Five", "Six", "Seven", "Eight", "Nine"]
second_list = my_list[3:8]
print(second_list)
It is important to remember that indexing returns a single element, while slicing returns a list.
Prep 6.4: Lists Are Mutable#
You have seen how indexing and slicing can be used to access the element or elements in a list.
Now, try running the following code.
my_list = ['uno', 'dos', 'tres', 'cuatro', 'cinco']
my_list[0] = 'one'
print(my_list)
my_list[-1] = 'fem'
print(my_list)
my_list = ["Zero", "One", "Two", "Three", "Four",
"Five", "Six", "Seven", "Eight", "Nine"]
my_list[3:8] = ['3', '4', '5', '6', '7']
print(my_list)
You can use indexing and slicing to change the value of an element or a range of elements in a list. This means that you can modify the list after it has been created. We say that lists are mutable.
Try these examples of modifying lists.
this_list = ["apple", "banana", "cherry"]
this_list[3:] = ["blackcurrant", "watermelon"]
print(this_list)
this_list[1:3] = []
print(this_list)
While the examples you just tried might look like reassignments, they are not: you were modifying the list, not creating a new one. This was not possible with integers, floats, or strings because they are immutable.
When you reach the examples on referencing lists, you will see other consequences of lists being mutable.
Prep 6.5: Functions for Lists#
Try running the following code.
digits = [3, 117, 4, -1.9, 5, 9.8, 0]
print("Length of digits:", len(digits))
print("Sum of digits:", sum(digits))
print("Minimum of digits:", min(digits))
print("Maximum of digits:", max(digits))
print("Any:", any(digits))
print("All:", all(digits))
If you are unsure what the functions len
, min
, max
, and sum
do, make changes in the code and run it again.
Now, try to run this code.
test_1 = 'Anna' == 'Anna'
test_2 = 'Anna' == 'anna'
test_3 = 'Anna' == 'ANNA'
test_4 = 'Anna' == 'Anne'
all_tests = [test_1, test_2, test_3, test_4]
print('All tests:', all(all_tests))
print('Any tests:', any(all_tests))
Print some of the elements from all_tests
. What type are they?
Prep 6.6: List Operators#
Try running this code.
list1 = ['water', 'earth']
list2 = ['fire', 'air']
elements = list1 + list2
print(elements)
elements = elements + list2
print(elements)
elements = elements + 4 * list1
print(elements)
Make sure you understand how the operators +
and *
work with lists. Also, note in these examples, lines starting with elements =
are reassignments.
Now, try to predict what the following code will print. Run the code to check your answer.
spam_list = ["SPAM"] * 5
print(spam_list)
the_best_number = [216] * 7
print(the_best_number)
concat_list = spam_list + the_best_number
print(concat_list)
Prep 6.7: List Methods#
Carefully examine this code and run it.
my_list = [3, 1, 4, 1, 5, 9, 2]
print(my_list)
my_list.sort()
print(my_list)
In the example above, sort()
is a method that operates on the list my_list
. This is the first time you are encountering a method in Python, so we need to explain some new syntax and learn some new terminology.
Later in the course, when we reach classes and object-oriented programming, you will learn how to define methods. For now, you should know that a method is very similar to a function. However, a method is written to be used with a specific object. In this case, the object is a list, and for now, you can think of an object as a special (slightly more complex) type of variable.
We execute functions by calling them, and the similar term for methods is to invoke them. A method is invoked (executed, run, used) by writing a variable name, followed by a period .
, followed by the method name, and finally parentheses with or without arguments.
Look at the code above and identify the lines where the methods are invoked.
A method always operates ton exactly one object, in our case, a lis. What is the name of the list that the method sort()
operates on in the code above?
Look at the code above and confirm that:
The list was modified by the method. To confirm this, look at the printed output.
It is still the same list. To confirm this, notice that we had no reassignments in the code.
Try running this code:
my_list = [3, 1, 4, 1, 5, 9, 2]
print(my_list)
my_list = my_list.sort()
print(my_list)
What can you conclude from the printed result? What does the sort()
method return?
Methods may take arguments and may return a value. Below are code snippets where we use some list methods. For each code snippet, identify the method and look at the printed output to see what the method does. Try checking whether the method takes any arguments, and whether it returns a value.
my_list = ["A", "B"]
my_list.append("C")
print(my_list)
models = ['linear', 'quadratic', 'power', 'exponential']
models.pop(1)
print(models)
models.pop(1)
print(models)
my_list = ["A", "B"]
my_list.extend(["C", "D"])
print(my_list)
names = ["Clara", "Alice", "Bob", "David", "Anna", "Eve", "Elvis", "Aaron"]
names.sort()
print(names)
my_list = ["A", "B", "A", "C", "a"]
print(my_list.count("A"))
my_list = ["A", "B", "C", "A"]
print(my_list.index("A"))
print(my_list.index("C"))
Try answering the following questions. If needed, modify the code and run it to check your answers.
What is the argument of the
pop()
method? Does it run without an argument? Doespop()
return a value?What happens if you use
append()
instead ofextend()
in the code above?Can
append()
take more than one argument? Canextend()
take more than one argument?What if the argument of
count()
is not in the list? What doescount()
return in that case?What if the argument of
index()
is not in the list? What doesindex()
return in that case?
Prep 6.8: Referencing Lists#
Carefully read the following code, and make a prediction of what will be printed. Run the code and examine the output.
a = ['one', 'two', 'three']
b = a
print(a)
b[1] = '2'
print(a)
What you have seen should surprise you.
Here is the explanation of this surprising behavior. After you make an assignment b = a
, both a
and b
point to the same list. And since lists are mutable, you can make changes to this list that are visible in both a
and b
.
Now look at this code and try to predict what it will print. Run the code to check your answer.
a = ['one', 'two', 'three']
b = a
a.append('four')
print(b)
Try now to predict what the following code will print. Run the code to check your answer.
a = ['one', 'two', 'three']
b = a
a = a + ['four']
print(b)
What you have seen might be confusing. When do a
and b
point to the same list?
The difference in the last two blocks of code is that append()
modifies the existing list, while reassigning creates a new list. So, in the last block of code, the line starting with a =
created a new list. It might be difficult to remember how exactly Python handles different cases, and the best strategy is to always check by printing the elements of the list if you are unsure.
Try this code now.
a = ['one', 'two', 'three']
b = a.copy()
a.append('four')
print(b)
The copy()
method creates a separate list with the same elements as the original list.
Prep 6.9: Iterating Over Lists#
Try running the following code.
weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
for i in range(len(weekdays)):
print('Hello ' + weekdays[i])
What you’ve just done is called iterating over a list or traversing a list.
Traversing a list is so common that Python has a special syntax for it. Check it in the next code block. Check it in the next code block.
weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
for weekday in weekdays:
print('Hello ' + weekday)
Let’s say that you want to print a different message for some of the elements in the list. And say also that you want to create a new list that contains the lengths of the strings in the original list. Run this code to see how it can be done.
weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
lengths = []
for weekday in weekdays:
lengths.append(len(weekday))
if weekday == "Monday":
print("I hate Mondays...")
else:
print("I like", weekday + "s")
print(lengths)
Iterating directly over elements of the list is practical. However, if you wanted to change their values, you need to iterate over indices.
Try this code where you loop and change the elements of the list.
distances = [12.8, 10.7, 6.6, 9.9, -0.2, 5.5, -0.3, 14.7]
for i in range(len(distances)):
if distances[i] < 0:
distances[i] = 0.0
print(distances)
Run a few more examples of iterating over lists.
recorded_data = [3.0, 4.2, -1, -1, 2.5, 3.7, -1000]
positive_data = []
for value in recorded_data:
if value > 0:
positive_data.append(value)
print(positive_data)
weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
temperatures = [20, 21, 17, 22, 24]
for i in range(len(weekdays)):
print(weekdays[i] + ":", temperatures[i], "degrees")
Prep 6.10: List Constructors#
Try running this code.
vowel_string = "aeiou"
vowels = list(vowel_string)
print(vowels)
Now, try this.
numbers_range = range(20)
numbers = list(numbers_range)
print(numbers)
As you can see, lists can be created from other objects.
Prep 6.11: Functions Modifying Lists#
So far, you have written functions that print something or return a value. Try now running this code, where the function does not have a return statement.
def modify_list(my_list):
my_list.append("banana")
fruits = ["apple", "orange"]
modify_list(fruits)
print(fruits)
As you can see, you can modify the list by passing it to a function and making changes to it inside the function. You could have returned the modified list, but modifying the list directly is simpler.
Try to change the function modify_list
such that it performs the following modifications, without returning the list:
Removes the first element of the list.
Sorts the list.
Extends the list with
["grape", "pear", "kiwi"]
.
Prep 6.12: The in
Operator#
Run this code.
some_list = [1, 2, 3]
print(5 in some_list)
print(1 in some_list)
condition = 13 in [1, 2, 3, 4, 5]
print(condition)
vowels = ['a', 'e', 'i', 'o', 'u']
print('y' in vowels)
As you can see, the in
operator checks if an element is in the list. The result is a boolean value.
Now, execute these examples of using the in
and not in
operators. Remember the syntax of conditional statements: the keyword if
, followed by a condition, followed by a colon, and then an indented block of code. Identify the conditions in the code below.
for energy_source in ['wind', 'coal']:
if energy_source in ['solar', 'wind', 'hydropower', 'geothermal', 'biomass']:
print(energy_source, "is a renewable energy source.")
else:
print(energy_source, "is not a renewable energy source.")
cat_family = ['lion', 'tiger', 'leopard', 'cheetah']
animal = 'bear'
if animal not in cat_family:
print(animal + " is not in the cat family.")
Prep 6.13: Fix List Problems#
The code below has a few common problems that can occur when working with lists. However, they do not actually cause an error. Can you spot the problems and fix them?
common_allergies = ['peanuts', 'shellfish', 'dairy', 'eggs', 'gluten']
common_allergies = common_allergies.append('soy')
print(common_allergies)
Solution
The append()
method modifies the list in place and does not return a list. Therefore, common_allergies.append('soy')
evaluates to None
in the assignment, which overwrites common_allergies
. Fix the code by removing the assignment and just keeping the method call.
danish_vowels = ['a', 'e', 'i', 'o', 'u', 'y']
english_vowels = danish_vowels[:-1]
danish_only_vowels = danish_vowels[5:5]
print(english_vowels)
print(danish_only_vowels)
Solution
The second slice danish_vowels[5:5]
returns an empty list because it is contains elements starting from index 5 but not including index 5. You should change the slice to danish_vowels[5:6]
. Similarly, range(5, 5)
will be an empty range, while list(range(5, 6))
is [5]
.
afternoon_activities = ['study', 'work', 'exercise', 'relax']
afternoon_activities.extend('sleep')
print(afternoon_activities)
Solution
The problem is that the extend()
method expects an iterable (like a list) as it’s argument, so 'sleep'
is treated as a sequence of characters. You can change 'sleep'
to ['sleep']
or use the append()
method, which expects a single element.
Prep 6.14: Fix List Errors#
Below are snippets of code that cause an error. Try to figure out what the error is and fix it.
list = ['x_ray', 'mri', 'ct_scan']
numbers = list(range(1, 6))
Solution
The error message says that the error was encountered in the second line, where it is trying to call the function list
. However, the origin of the error is in the first line, where the built-in list
function is overwritten by the variable list
. You should never name a variable list
because it is a built-in function in Python. Here, the assignment shadows the built-in function list
. To fix the issue, rename the variable to something else, e.g. scan_list
.
my_favorite_functions = ['max', 'min', 'sum', 'len']
print("The length of a list can be estimated with", my_favorite_functions[4])
Solution
In this case, the error message is clear. The list my_favorite_functions
has 4 elements, and the index of the last element is 3. Therefore, the index 4
is out of range. To fix this, you should change the index to 3
.
spooky_electronics = ['resistor', 'capacitor', 'inductor', 'transistor', 'diode']
spooky_electronics.pop('resistor')
Solution
The pop()
method expects an index as an argument, but you are passing a string. You should pass the index of the element you want to remove, e.g. spooky_electronics.pop(0)
. If you don’t know the index of the element you want to remove, you can use the remove()
method instead, e.g., spooky_electronics.remove('resistor')
.
pi_digits = [3, 1, 4, 1]
# removes all even numbers from the list
for i in range(len(pi_digits)):
if pi_digits[i] % 2 == 0:
print("Popping", pi_digits[i], "at index", i)
pi_digits.pop(i)
print(pi_digits)
Solution
The error is caused because you are modifying the list while iterating over it. When starting the loop, you have 4 elements in the list, and the loop goes from 0 to 3. However, when you remove one element, the list is shortened and index 3 no longer exists. One way to fix the error is to iterate over the list in reverse order. Another way is to create a new list with the elements you want to keep, instead of modifying the original list.
print(max([1, "2", 3]))
Solution
The error is caused because the max
function expects all elements to be of the same type. In this case, the list contains an integer, a string, and another integer.
parts = ['A1', 'A2', 'A3', 'B1', 'C1', 'C2']
partsA = parts[:3]
partsB = parts[3]
partsC = parts[4:]
parts_again = partsA + partsB + partsC
Solution
The error is caused by the parts being different types. The first part is a list, the second part is a string, and the third part is a list. This happened because you extracted partB
using indexing instead of slicing. To fix the error, you should change the indexing to slicing, e.g., partsB = parts[3:4]
.
Prep 6.15: Predict the Output#
For each of the following code snippets, try to predict what will be printed before running the code. Then run the code and observe the output.
shrek_movies = ['Shrek', 'Shrek 2', 'Shrek the Third']
print(shrek_movies.append('Shrek Forever After'))
print(shrek_movies)
print(len(['water, earth, fire, air']))
print(len(['water', 'earth', 'fire', 'air']))
print(len([['water', 'earth', 'fire', 'air']]))
my_list = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
print(my_list[1:4])
print(my_list[4:1])
print(my_list[4:1:-1])
greetings = ["Hello", "Hi", "Hey", "Hola", "Bonjour"]
print(greetings.pop())
Self quiz#
Question 6.1#
What is the length of the list my_list
after the following code is executed?
my_list = ["12345"]
Question 6.2#
What gets printed when the following code is executed?
my_list = ['apple', 'banana', 'cherry']
print(my_list[1:2])
Question 6.3#
What happens when executing this code?
my_list = ['apple', 'banana', 'cherry']
print(my_list[-1])
Question 6.4#
What happens when executing this code?
data = ["Mike", "Bike", "Michael"]
print(data[len(data)])
Question 6.5#
Which of the code snippets is equivalent to the following code?
my_list = [1, 2, 3, 4, 5]
Question 6.6#
What is the output of the following code?
courses = ["Coding", "Math", "Physics", "Chemistry"]
print(courses[1:-1])
Question 6.7#
What are the types of the variables x
and y
after this code is executed?
perfect_squares = [1, 4, 9, 16, 25]
x = perfect_squares[2]
y = perfect_squares[2:3]
Question 6.8#
Based on the following list definition, which of the following expressions evaluate to True
?
data = [1, 0, -1, 0, 1, 0, -1]
Question 6.9#
Which code correctly adds the element "guanine"
to the list dna_bases
?
dna_bases = ["adenine", "thymine", "cytosine"]
Question 6.10#
You have defined
planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter",
"Saturn", "Uranus", "Neptune", "Pluto"]
but now you want to remove "Pluto"
from the list. Which of the following lines will achieve this?
Question 6.11#
What is the index of the element "Earth"
in the list rocky_planets
?
rocky_planets = ["Mercury", "Venus", "Earth", "Mars"]
Question 6.12#
What is the value of chess_pieces
after the following code is executed?
chess_pieces = ["King", "Queen", "Bishop", "Knight", "Rook", "Pawn"]
chess_pieces[3] = "Meow"
Question 6.13#
What is the length of the list my_list
after the following code is executed?
my_list = list(range(1,4)) * 3
Question 6.14#
Which of the lists list1
and list2
contains the element 1
after the following code is executed?
def add_element(lst):
lst.append(1)
list1 = []
list2 = list1
add_element(list2)
Question 6.15#
What is the index of the element 'Corgi'
after the following code is executed?
dogs = ["Beagle", "Corgi", "Akita"]
dogs.sort()
Question 6.16#
What is the value of count
after the following code is executed?
materials = ['concrete', 'steel', 'wood', 'glass', 'aluminum']
count = 0
for material in materials:
if len(material) > 5:
count = count + 1
Question 6.17#
What is the value of check
after this code executes?
check = 'Milan' in ['Rome', 'Milan', 'Venice']
Question 6.18#
In how many of the following lists is the element "hazelnut"
found?
nuts1 = ["almond", "walnut", "hazelnut"]
nuts2 = ["Hazelnut", "Pecan", "Pistachio", "Macadamia"]
nuts3 = ["cashew, hazelnut, brazil"]
Question 6.19#
What is stored in the variable important_birthdays
after the following code is executed?
important_birthdays = list(range(17, 19)) + list(range(10, 50, 10)) + [100]
Question 6.20#
What is the value of the variable a
after the following code is executed?
a = ['D', 'C', 'B']
b = a
b[0] = 'A'
Question 6.21#
What is the value of moons
after this code executes?
moons = ["The Moon", "Phobos", "Deimos", "Europa", "Titan"]
moons = moons[1:]
moons = moons[1:]