CS 1110 - Introduction to Programming
Starting Out with Python
Chapter 7, Lists and Tuples
This lesson covers chapter 7, which teaches us a new word,
and a new meaning for an old one. Objectives important to this lesson:
- Sequences and lists
- List slicing
- Finding objects in lists: the in operator
- List methods
- Copying lists
- Processing lists
- Two-dimensional lists
Sequences and Lists
The text gives us two definitions that are used in this
- Sequence - a
sequence is an object that can hold other objects, usually things that
belong together for some reason. A string,
for instance, is a sequence of
- Lists - a
list is a sequence of objects
that can be of any data type.
Consecutive elements (items in
the sequence) can be different
data types. The contents of a list can be changed by a program,
including adding and removing elements.
When a list is created, the elements are enclosed by one set
of square brackets, and the series of elements is delimited by commas.
That's a long way to say that they look like this:
five_integers = [1, 2, 3, 4, 5]
Since a list is allowed to contain multiple data types, we
could have a list that includes a combination of integers, strings,
floats, etc. Often, you will want a list that contains elements of the
same data type. The text offers an example that seems more valuable for
numbers = list(range(100))
In this example, the range()
function will generate 100 numbers, from 0 to 99. Yes, you could also
do it as range(1, 101) to get 1 to 100. They will be constructed as an iterable, which looks like a set,
and could be used as the value list of a for-in loop. This may be confusing,
in that iterables are written the same way that lists are written. That
set of numbers is handed to the list()
function, which creates a list holding those numbers, which is then assigned as the value of the
variable called numbers, which
is now the name of a list.
This would beat typing out all those numbers to create the list by hand.
The text explains that we can use the * symbol in an
expression with a list on its left, and an integer on its right, to
repeat the list the number of times represented by the integer. For
example, assume we has a list created like this: list_of_five = [1, 2, 3, 4, 5] We might use the
repetition operator (the *) like this:
new_list = list_of_five * 3
This would create a new list called new_list that would
contain [1, 2, 3, 4, 5, 1, 2, 3, 4,
5, 1, 2, 3, 4, 5]. The new list would contain the original list
three times. The text does not mention it immediately, but this method
can be used to create a list you intend to use and fill with real
values later. Filling it with dummy values makes sure that the list
exists and is the size you intend it to be. I could create a list to hold twenty elements, full of 0s for now, like this:
master_list =  * 20
Since a list does in fact look exactly like an iteration in a for-in loop, it
should not be a surprise that we can use the name of a list to serve as an
iteration. If I build a for-in loop with the new_list created above, it
could begin like this:
for coconut in new_list:
print(coconut + '\n')
The loop you see above would run fifteen times, and it would
print the current value of coconut and a newline character each time.
The text mentions in passing that Python does not have arrays,
since lists will do what arrays do and more besides that. We will
reserve judgment about that for a bit, but will point out that the
elements in a list have something in common with the elements in an
array. We can reference them using index notation. Each element in the new_list example can be accessed as new_list[index_number]. Reading the
list from left to right, the index numbers would start at 0, and run
through one less than the number of elements.
If we wanted to access the list items from right to left, there is a notation
for that. The item at the end of the list can be accessed as list_name[-1]. The next one's index would be -2, and so on.
Remember that indexes from
left to right start at 0. The text warns us not to use code like
beatles = ['John', "Paul',
'George', 'Stu', 'Pete', 'Ringo']
index = 0
while index < 7
index += 1
This would raise an exception error, an IndexError exception, because it
would try to print 7 times, index values 0 through 6, and there
is no element whose index is 6. If you don't understand that, back
up four paragraphs and try it again.
It may occur to you that it would be handy to know how long a
list happens to be, since the first index is always 0 and the last one
is always "number of elements minus 1". The text tells us to use the len() function for this purpose.
Pass the name of a sequence to
the len() function. You will
get a count of the elements
in that sequence as a return value.
To make the code example above run correctly, we might rewrite
the conditional line like this:
while index < len(beatles)
We can also assign values
to the elements of a list, using the list[index] notation as we would
use a variable name.
The text tells us that lists are mutable,
which means we can assign new values to the various indexed positions
in the list. You should have had no reason to think otherwise, but it
is nice to know this is so, because we will be told the opposite about
tuples, later in the chapter.
Another way to make larger lists is to concatenate two or more
lists. We use the + operator to do so, but we are not doing math.
list3 = list1 + list2
The line above means to add the contents of list2 to the end
of the contents of list1, then assign the entire list to list3. If we
had only meant to add the elements of list2 to the end of list1 we
could have written list1 += list2. In this example, we are
still concatenating, but we are changing list1 instead of creating a
Slicing means to select a subset of the elements in a
list, which will be described by a sequence of indexes (or indices,
depending on your grammar
preference). In the example in the text, we have a list of the
days of the week, Sunday through Saturday, stored in a list named days.
If we wanted a slice of those elements stored in a new list we will
call week_days, we could write a line like this:
week_days = days[1:6]
This notation is not very logical, but it's how the thing
works. It means that week_days
will hold copies of the elements of days
having index values of 1, 2, 3, 4, and 5, stopping
at and not including index 6. The text goes on to offer more
examples, but confuses the issue by using lists of numbers.
- Just remember that the first number in a slice
notation is the first index value that is used, the the second
number in a slice is the stop value, which is not
included in the slice.
- If there is no stop value in a slice, such as days[1:],
that would mean to start at index 1, and go to the end
of the list.
- If there is no start or stop value, that means to
use the whole list, which means there was no point to taking
- If there are three numbers in the slice notation,
the third number is a step value. For example we could type even_days = days[1:6:2]. This would
select days starting at index,
counting by twos, and having 6 as the stop value.
- As if that were not enough, you can use a negative
number as the start value, which is reckoned as an offset
from the end of the list, not the beginning of it.
- There are other rules we will leave for another day.
Sufficient unto the day is the evil thereof.
Finding Objects in Lists: the in operator
The text tells us that we can verify whether a values exists
in a list with the structure value in
list_name, which is a lot shorter than running a loop and
checking all the values stored in a list. The first example is in example program 7-2. The program
creates a list and populates it with four strings. The program then
asks the user for a string to look for. The program assigns the user's
string to a variable called
search, and runs this code:
if search in
print(search, 'was found in
print(search, 'was not found
in the list.')
As the text explains, the phrase value in list_name will return a True if the value is found, and will return a False if the value is not found. You can use not in, instead of in, if you want
to test for a value being missing
from a list.
List Methods and Useful Built-in
As you can see in the last example, Python has some features
that relate to lists. Table 7-1 shows us six methods. I have modified
the table below to include code examples, which I think will help more
than the information the table in the text gave.
Calls the append() method of a list to add a new item to the end of the list.
Calls the index() method of a list, which will return the index of the first instance of a specified value. Note: this will raise an
exception if the value is not found, so you may want to verify that the
item is in the list first.
Calls the insert() method of a list, which inserts the new item at the
specified index. The existing item at that index is moved to the next
higher index, and the same is done for each of the other items in the
list that were to the right of the insertion position.
Calls the sort() method of a list, which reorders the items in the list
according to the rules for < and >. Note that this changes the
actual list, not a copy in a variable. If you want to retain the
original order, make a copy first.
Calls the remove() method of a list, which finds and deletes the first instance of the specified
value in the list. Items in the list that followed the removed item are
reassigned new indexes, starting with the index of the removed item.
Note: this will raise an exception if the value is not found, so you
may want to verify that the item is in
the list first.
Calls the reverse() method of a list, which reverses the order of the
elements in the list. Note that this changes the actual list, not a
copy in a variable. If you want to retain the original order, make a
copy first. (See below.)
The text discusses three more tools that are useful with lists:
- del - The del keyword is used to delete a specific item in a list. It
may look like this:
- min() - The min()
function takes a list name as its argument, and returns the element
with the lowest value. It may look like this:
- max() - The max() function takes a list name as its
argument, and returns the element with the highest value. It may look
Making a copy of a
list is more difficult that you might think. Before we start, think
about this: what is a list?
When we are working with a list in a program, a list is a set of values stored in memory.
Consider this example:
list1 = ['a', 'b', 'c']
list2 = list1
The text informs us that the code in the lines above will
create and populate list1, then set the label list2 to point to the
same list in memory. The new list name, list2, does not create a new list. Essentially,
this code simply creates an alternate
name for the same list.
The text then shows us two ways to make an actual copy of a
list. The first way is to use a loop and a list method. It could look
list1 = ['a', 'b', 'c']
list2 =  # this creates an empty list
for item in list1
list2.append(item) # this
method appends each item from list1 to list2, and it avoids having to
write to indexes that do not exist in the empty
way would have been to measure the length of list1, create a new list
with the same number of (dummy) elements, then loop through list1,
copying the value of each indexed element to the corresponding element
in list2. The second way the text shows us is shorter, but the result
is the same:
list1 = ['a', 'b', 'c']
list2 =  + list1
# this creates a new empty list,
then appends the contents of
list1 to the new list
In both methods above, a new empty list is
created first. This is a necessary step if we want there to be two
actual lists in memory when we are done.
- In example program 7-7, baristapay.py, the text starts the
program knowing that there will be six baristas who work for our
company, so it sets a constant, NUM_EMPLOYEES
- It then creates a list called hours that holds 6 zeros.
- The program then runs a loop that asks for the hours of
each employee, and writes that number as a float in the corresponding
element in the list.
- The program asks the pay rate, given that the same rate is
used for all employees. This stored in a variable.
- The program then runs a second loop through the list,
calculating and printing the gross pay earned by each employee.
The text remarks on the usefulness of using a constant for the
number of employees, since it makes it very easy to change that one
line of the program to accommodate a change in that number.
The text shows us an example of a program that runs through a
list of numbers and calculates their total.
This is done again in the next program example, but this time the
program calculates the average
of the numbers (mean,
actually), by using the len() function
to count the number of elements in the list.
The text pauses to consider how you might pass a list to a function as an argument.
Having asked the question, you may be disappointed that there is no
trick to it. You simply pass the name of the list and receive it in the
called function as you would pass and receive any variable. It works
the same way when a list is created and populated in a called function.
That function can return the name of the list, and it will be
accessible by the calling function.
The text introduces a file
object method called writelines(),
which can be used to write a list
to a file. In example program 7-13, writelines.py, the program creates
a list, cities, that holds
four strings. It then opens a file, cities.txt,
in w mode. It then calls the
writelines method of the file object, outfile,
to write the list to the file:
The file is then closed, and that example is done. The problem
with that method is that the data goes into the file as one string,
without any clue to where the individual strings should break. The text
presents an alternative method: use a loop
that can use the file object write()
method to write each element of the list, concatenated with a newline character, which will put
each element on a line by itself. This works better for our purposes.
On page 322, we are introduced to the readlines() method of a
file object. This method reads each line in a file, and returns a list
of strings, each string being a single line. As we have seen before, we
may or may not want the newline character that ends each string in the
data. If we do not want it, there is an example in program 7-15 that
list's rstrip() method to
strip off that trailing character from each string in the list. Program
7-16 does the reverse: it takes a list of numbers, turns them into
strings, concatenates a newline on each string, and writes each string
to a file. Program 7-17 shows how to read all the lines in a file into
a list, then run a loop to read each element, convert it to an int,
then overwrite the string version with the int version in the list. We
can talk about these examples in class.
We have seen lists in this chapter that held numbers, and
lists that held strings. The text explains that any object can be an
element in a list, including other lists. A two-dimensional list is
just a list whose elements are also lists. The text shows us that
printing any element of such a list will print the entire list that is
stored at that element. That's nice, but what if we only wanted to
access some of the elements of one of those nested lists? The text
introduces an idea that leads to a notation that will let us access
what we please.
In the example in the text, we start with a list called
students. It holds three elements, each of which is a list of two
students. The text represents this as a table with two columns, the
size of the nested lists, and three rows, the number of lists in the
|data held in students list
|students = 'Joe'
|students = 'Kim'
|students = 'Sam'
|students = 'Sue'
|students = 'Kelly'
|students = 'Chris'
Thinking about it this way, it should be more obvious why this
kind of list is called two-dimensional. Each row holds one of the nested
lists. Each column holds the elements at a particular position in one of
the lists. As I have noted in the table, you can reference each element
by the name of the two-dimensional list, followed by two subscripts,
the first one for the row, and the second one for the column. Think of
those as being like x and y coordinates on a graph. Horizontal
reference first, then vertical
reference. The text shows us another example, one having three columns,
and three rows. Note that this still uses two coordinates, one for the
row and one for the column. In program 7-18, the text shows us how to
fill such a bunch of lists with two loops, one inside the other. The
inner loop fills a row, while the outer loop controls what row is being
filled. We could ask the user for a value, or we could do as the text
does, generating a random value in each case.
The last topic we will cover in this chapter is tuples. The text explains that a tuple is like a list, but it is immutable. Think of it as a list that is also a constant.
There is a notation difference as well. The elements of a list are
enclosed in square brackets. The elements of a tuple are enclose in
my_list = [1, 2, 3]
my_tuple = (1, 2, 3)
The text demonstrates looping through a tuple (to read it, because you can;t modify it) using two notations:
for n in my_tuple:
for n in range(len(my_tuple)):
The text tells us that tuples will support most of the things we can do with lists, but tuples do not contain these methods:
Showing some sensitivity to the reader, the text asks "what is
the point of having tuples?" For some unspecified reason, you can
process tuples faster to process than you can process lists. The author
seems to like tuples because you can't change them, making them safer
for testing. Maybe, but that still doesn't sound like a good reason to
- Should you need to turn a tuple into a list, call the list() function with the name of the tuple as your argument.
- Should you need to turn a list into a tuple, call the tuple() function with the name of the list as your argument.