11  Lists of things

This chapter is about lists and dictionaries that are Python values that can contain other Python values. Lists and dictionaries let you build relationships between values, which is what data structures represent.

Lists

For many kinds of data, the order of things is important. Just like the order of characters is important for the meaning of the text in a string, we sometimes want to specify the order of other things because the relative order of items in the list has some meaning. It could be a grocery list where you have listed the things to buy in the order you get to them in the supermarket. This is where Python lists are helpful. When you print a list, it nicely shows all the values it contains.

grocery_list = ["salad", "canned beans", "milk", 'beer', 'candy']
print(grocery_list)

Unlike strings that can only store the order of characters, lists can contain any kind of values, and you can mix different types of values in any way you like. Here is a list that contains an integer, a boolean, a string, and a list:

mixed_list = [42, True, 'programming', [1, 2, 3] ]

By now, you have probably guessed you will make a list with two square brackets. Between them, you can put values with commas in between. A list is a container of other values, and the value of the list itself does not depend on the values it contains. This makes sense. Otherwise, an empty list would not have a value:

my_list = []

You can add single values to the end of a list using the append method of lists. Try it out:

desserts = []
print(desserts)
desserts.append('Crepe suzette')
print(desserts)
desserts.append('Tiramisu')
print(desserts)
desserts.append('Creme brulee')
print(desserts)

If you have a list you want to add to the end of another list, you use the extend method:

cheeses = ['Gorgonzola', 'Emmentaler', 'Camembert']
desserts.extend(cheeses)
print(desserts)

Notice how append and extend modifies the existing list instead of producing a new list with the added element.

Exercise 11-1

Do you think this will work?

cheeses = ['Gorgonzola', 
           'Emmentaler', 
           'Camembert']
print(cheeses)

Surprised? Code inside parentheses, brackets, and braces can span several lines, sometimes making your code easier to read.

Exercise 11-2

You use the’ in’ operator to test if a value is in a list. Try this:

print('Tiramisu' in desserts)
print('Meatloaf' in desserts)

Exercise 11-3

You can concatenate two lists to produce a new joined list. Make sure you figure out how this works before you try it. Then, experiment with changing the lists. Can you concatenate two empty lists?

some_list = [1, 2, 3]
another_list = [7, 8, 9]
merged_list = some_list + another_list
print(merged_list)

This is yet another example of how the functionality of Python objects lets them “know” how to behave under different circumstances, such as when adding two objects (see Section 10.0.0.11).

Exercise 11-4

What do you think is printed here? Make sure you figure out how you think this works before you try it out. What does the append method return?

my_list = []
x = my_list.append(7)
print(x)
print(my_list)

Indexing and slicing lists

Now you know how to make lists, but to work with the values in lists, you must also know how to access the individual values a list contains. Fortunately, indexing lists work just like indexing strings: Each value in a list is identified by an index exactly like each character in a string:

numbers = [7, 4, 6, 2, 8, 1]
print("first value is", numbers[0])
print("second value is", numbers[1])
print("third value is", numbers[2])

Notice that the function len can also compute the length of a list. So you also get the last value in a list like this:

numbers = [7, 4, 6, 2, 8, 1]
last_index = len(numbers)-1
print("Last element is", numbers[last_index])

If you want a sub-list of values from a list (we also call that a slice), you specify a start index and an end index separated by a colon, just like with strings:

print(numbers[1:4])

When you run that, you can see that numbers[1:4] is substituted for [4, 6, 2], so the slicing operation produces a new list of the specified values.

Exercise 11-5

What do these two expressions reduce to?

[11, 12, 13, 14, 15, 16, 17][2]

Exercise 11-6

What is printed here? Do all the substitution and reduction steps and compare them to the exercise above.

l = [11, 12, 13, 14, 15, 16, 17] 
print(l[2])

Exercise 11-7

What is printed here? Do all the substitution and reduction steps — and do them twice. Next week, you will be happy you did.

numbers = [1,2,3]
i = 0
print(number[i])
i = 1
print(number[i])
i = 2
print(number[i])

Exercise 11-8

What do you think happens here? Make up your mind and try out the code below:

l = [11, 12, 13, 14, 15, 16, 17] 
l[4] = "Donald"
print(l)

Were you surprised by what happened? Compare to Section 10.0.0.15. Lists are not immutable like strings, and you can replace values by assigning a new value to an index in the list.

Exercise 11-9

With your knowledge of slicing, what do you think is printed below:

l = [11, 12, 13, 14, 15, 16, 17]
print(l[:3])
print(l[3:])
print(l[:])

Exercise 11-10

What do you think happens when you specify an index that does not correspond to a value in the list:

l = [11, 12, 13, 14, 15, 16, 17] 
print(l[7])

Read and understand the error message. Does it ring a bell?

Exercise 11-11

Do you also get an error when you specify a slice where the end is too high? Try it out:

l = [11, 12, 13, 14, 15, 16, 17]
print(l[4:99])

I guess that is also worth remembering.

Exercise 11-12

Which value in a list named l does this expression reduce to?

l[len(l)-1]

Exercise 11-13

If you do not like Emmentaler, you can delete it. What does the del keyword do?

cheeses = ['Gorgonzola', 'Emmentaler', 'Camembert']
print(cheeses)
del cheeses[1]
print(cheeses)

Exercise 11-14

Because intervals are “ends exclusive,” we can compute the length of a slice as end - start:

l = [7, 4, 6, 2, 8, 1]
start = 1
end = 4
print("{} has length {}".format(l[start:end], end-start))

Consider what this code would look like if ends were included in intervals.

Exercise 11-15

Another advantage of “ends exclusive” intervals is that you only need one index to split a list in two:

numbers = [1, 2, 3, 4, 5, 6]
i = 3
beginning = numbers[:i]
end = numbers[i:]
print(beginning + end)

If ends were included in intervals, this would be more complex.

Exercise 11-16

Do all the substitution and reduction steps in your head (or on paper) before you write any of the following code. Think carefully and decide what you think will be printed below. Remember that the value of a list is a container that holds other values in it. Then, write the code and see if you are right. If you were not, figure out what led you to the wrong conclusion.

x = 'A'
y = 'B'
z = 'C'
lst = [x, y, z]
print(lst)

x = 'Preben'
print(lst) # what is printed here? 

lst[0] = 'Mogens'
print(lst) # what is printed here?

Exercise 11-17

Do you remember this trick from string slicing?

l = [1, 2, 3, 4, 5]
print(l[::-1])

Exercise 11-18

You can produce a list by splitting a long string into smaller parts. Think: “Hey string, split yourself on this smaller string”. Try these variations to figure out how it works.

"Homo sapiens neanderthalensis".split(" ")
"Homo sapiens neanderthalensis".split('en')
'ATGCTCGTAACGACACTGCACTACTACAATAG'.split('')
"1, 2, 3, 5, 3, 2, 5, 3".split(', ')
"1,2,3,5,3,2,5,3".split(',')
'ATGCTCGTAACGACACTGCACTACTACAATAG'.split()
"Homo sapiens neanderthalensis".split()

Notice that the method has a default behavior when no argument is passed to it.

Exercise 11-19

You can produce a string by joining the elements of a list (if all the elements are strings, of course). Think: “Hey string, put yourself between all the strings in this list”.

"-".join(['Homo', 'sapiens', 'neanderthalensis'])
"...".join(['Homo', 'sapiens', 'neanderthalensis'])
"".join(['A', 'T', 'G'])

Notice how you can join something on an empty string. This is a very useful technique to turn a list of characters into a string.

General exercises

What does this expression reduce to? 'aaaaa', 'BaBaBa', or 'Banan'. Make up your mind, and then run the expression to check.

'a'.join('Banana'.split('a')[:3] * 4)[-5:]