5.2. Lists¶
5.2.1. A List is a Sequence¶
Like a string, a list is a sequence of values. In a string, the values are characters; in a list, they can be any type. The values in list are called elements or sometimes items.
There are several ways to create a new list; the simplest is to enclose
the elements in square brackets [
and ]
:
[10, 20, 30, 40]
['crunchy frog', 'ram bladder', 'lark vomit']
The first example is a list of four integers. The second is a list of three strings. The elements of a list donât have to be the same type. The following list contains a string, a float, an integer, and (lo!) another list:
['spam', 2.0, 5, [10, 20]]
A list within another list is nested.
A list that contains no elements is called an empty list; you can create
one with empty brackets, []
.
As you might expect, you can assign list values to variables:
The in
operator also works on lists in a similar fashion to how it works with strings.
An <a> in <b>
expression, in general, evaluates to True
if the <a>
value on the left of in
is found in the list (or other sequence) <b>
on
the right.
5.2.2. Indexing (Lists are Mutable)¶
The syntax for accessing the elements of a list is the same as for accessing the characters of a string: the bracket operator. The expression inside the brackets specifies the index. Remember that the indices start at 0:
Unlike strings, lists are mutable: you can change the order of items in a list or reassign an item in a list. When the bracket operator appears on the left side of an assignment, it identifies the element of the list that will be assigned.
The second element numbers
(at index 1), which used to be 123, is changed
to 5.
You can think of a list as a relationship between indices and elements. This relationship is called a mapping; each index âmaps toâ one of the elements.
Indexing in a list works the same way as indexing in a string:
Indexes start from 0.
Any integer expression can be used as an index.
If you try to read or write an element that does not exist, you get an
IndexError
.If an index has a negative value, it counts backward from the end of the list.
5.2.3. Traversing a List¶
The most common way to traverse the elements of a list is with a for
loop, which we have seen previously:
This works well if you only need to read the elements of the list. But if you
want to write or update the elements, you have to use indexing. The for loop
gives you a copy of each value in the list, but it provides no way to alter
them. A common way to do that is to combine the functions range
and
len
:
The sequence in this loop is the sequence of all valid indexes into the list. The expression range(len(numbers))
gives the length of the list to range()
, which produces a sequence of values starting at 0 and going up to, but not including, the length of the list itself. This is always going to be the valid indexes into the list!
The loop traverses the list and updates each element. Each time through the
loop, i
gets the index of the next element. The assignment statement in the
body uses i
to read the old value of the element and to assign the new
value.
Although a list can contain another list, the nested list still counts as a single element. The length of this list is four:
5.2.4. List Operations¶
Most of the operators that we have seen work with strings will work with lists just the same. You can expand your understanding of them as âlist operatorsâ to be âsequence operators,â working on sequences of values whether they be strings or lists.
The +
operator concatenates lists:
And the *
operator repeats a list a given number of times:
The slice operator also works on lists:
As with strings, if you omit the first index, the slice starts at the beginning. If you omit the second, the slice goes to the end. So if you omit both, the slice is a copy of the whole list. This seems pointless, but it is often useful when applied to lists!
Since lists are mutable, it is often useful to make a copy before performing operations that modify lists. Weâll see an example below.
5.2.5. List Methods¶
Like strings, lists are objects as well. And like with strings, this means that lists contain useful built-in methods that we can call using dot notation.
For example, the list method append()
adds a new element to the end of a
list:
The extend()
list method takes a list as an argument and appends all of the
elements (note that t2
is unmodified):
The sort()
method rearranges the elements of the list from low to high:
Most list methods are void methods; they modify the list and return None
.
If you accidentally write t = t.sort()
, you will be disappointed with the
result:
5.2.6. Deleting elements¶
There are several ways to delete elements from a list. If you know the
index of the element you want, you can use the list method pop()
:
pop()
modifies the list and returns the element that was removed. If you
donât provide an index, it deletes and returns the last element.
If you donât need the removed value, you can use the del
operator:
If you know the value of the element you want to remove (but not its index),
you can use remove()
:
The return value of remove()
is None
; it is a void function. So again,
donât try to write something like t = t.remove('b')
:
5.2.7. Lists and Functions¶
Weâve seen a number of built-in functions that can be applied to lists that allow you to quickly compute something from a list without writing your own loops:
Note that none of these functions are methods; we do not call them with dot notation. Instead, these are functions that accept a list as an argument.
The sum()
function only works when the list elements are numbers.
The other functions (max()
, len()
, etc.) work with lists of
strings and other types as well.
To show how these can be used, we can rewrite a program that computes the average of a list of numbers entered by the user using a list.
First, the program to compute an average without a list:
In this program, we have count
and total
variables to keep the
number and running total of the userâs numbers as we repeatedly prompt
the user for a number.
We could simply remember each number as the user entered it and use built-in functions to compute the sum and count at the end.
We make an empty list before the loop starts, and then each time we have a number, we append it to the list. At the end of the program, we simply compute the sum of the numbers in the list and divide it by the count of the numbers in the list to come up with the average.
5.2.8. Lists and Strings¶
A string is a sequence of characters and a list is a sequence of values,
but a list of characters is not the same as a string. To convert from a
string to a list of characters, you can use list()
:
Because list()
is the name of a built-in function, you should avoid using
it as a variable name. It is also good to avoid the letter l
, because it
looks too much like the number 1
. So thatâs why the code in this section
uses t
.
The list()
function breaks a string into individual letters. If you
want to break a string into words, you can use the split()
string method:
Once you have used split()
to break the string into a list of words, you can
use the index operator []
to get a particular word in the list.
You can call split()
with an optional argument called a delimiter
that specifies which characters to use as word boundaries. The following
example uses a hyphen as a delimiter:
The string method join()
is the inverse of split()
. It takes a list of
strings and concatenates the elements. It isnât called how you might expect it
to be, though. join()
is a string method, so you have to call it from the
delimiter string (that is, put the delimiter string on the left side of the dot
notation) and pass the list as an argument:
In this case the delimiter is the string '_-_'
, so join()
puts a copy
of that between each word. To join strings with spaces, use ' '
as the
delimiter. To concatenate strings (join them with nothing in between), you can
use the empty string, ''
, as a delimiter.
5.2.9. Objects and Values¶
If we execute these assignment statements:
a = 'banana'
b = 'banana'
we know that a
and b
both refer to a string, but it might not be clear
whether they refer to the same string. There are two possible states:
In one case, a
and b
refer to two different objects that have
the same value. In the second case, they refer to the same object.
To check whether two variables refer to the same object, you can use the is
operator. The expression A is B
will evaluate to True
if A
and
B
are the same object and False
if they are not. So to check the
situation of our 'banana'
strings:
In this example, Python only created one string object, and both a
and
b
refer to it.
But when you create two lists, even with the same elements in each, you get two objects:
In this case we would say that the two lists are equivalent, because they have the same elements, but not identical, because they are not the same object. If two objects are identical, they are also equivalent, but if they are equivalent, they are not necessarily identical.
Until now, we have been using âobjectâ and âvalueâ interchangeably, but it is
more precise to say that an object has a value. If you execute a = [1,2,3]
,
a
refers to a list object whose value is a particular sequence of elements.
If another list has the same elements, we would say it has the same value.
5.2.10. References and Aliasing¶
If a
refers to an object and you assign b = a
, then both variables will
refer to the same object:
The association of a variable with an object is called a reference. In this example, there are two references to the same object.
An object with more than one reference has more than one name, so we say that the object is aliased.
If the aliased object is mutable, changes made with one alias affect the other:
Although this behavior can be useful, it is error-prone. In general, it
is safer to avoid aliasing when you are working with mutable objects. This is
a case where making a copy of a list using slicing can be helpful! For
example, the following is just a slight modification of the above example, but
now altering b
doesnât affect a
:
5.2.11. Common Pitfalls¶
Careless use of lists (and other mutable objects) can lead to long hours of debugging. Here are some common pitfalls and ways to avoid them:
Donât forget that most list methods modify the argument and return
None
. This is the opposite of the string methods, which return a new string and leave the original alone.If you are used to writing string code like this:
word = word.strip()
It is tempting to write list code like this:
t = t.sort() # WRONG!
Because
sort()
returnsNone
, the next operation you perform witht
is likely to fail.Before using list methods and operators, you should read the documentation carefully and try them out a few times. The methods and operators that lists share with other sequences (like strings) are documented at https://docs.python.org/3/library/stdtypes.html#common-sequence-operations. The methods and operators that only apply to mutable sequences are documented at https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types.
Pick an idiom and stick with it.
An âidiomâ in programming is a particular style or way of accomplishing something. Part of the problem with lists is that there are too many ways to do things. Of the many options, make sure you consistently use just one throughout a program.
For example, to remove an element from a list, you can use
pop()
,remove()
, ordel
.To add an element, you can either use the
append()
method or the+
operator. Be careful, though. Donât forget that these are right:t.append(x) t = t + [x]
And these are wrong:
t.append([x]) # WRONG! t = t.append(x) # WRONG! t + [x] # WRONG! t = t + x # WRONG!
Try out each of these examples in the active code block below to make sure you understand what they do. Notice that only the last one causes a runtime error; the other three are legal, but they do the wrong thing.
Make copies to avoid aliasing.
If you want to use a method like
sort()
that modifies the argument, but you need to keep the original list as well, you can make a copy first.orig = t[:] # orig is a copy of t now t.sort() # t is now sorted, but orig is unmodified
In this example you could also use the built-in function
sorted()
, which returns a new, sorted list and leaves the original alone. But in that case you should avoid usingsorted
as a variable name!