We've seen that strings support several methods,
like count
, join
, lower
, and split
.
But other types of values have methods, too.
Lists are particularly notable examples.
We'll examine two methods that lists have.
The append
method is the most-used list method:
It modifies the list with the argument added at its end.
For instance, if nums
refers to the list
[2, 3, 5],
then nums.append(7)
leads the list to now be
[2, 3, 5, 7].
The pop
method deletes something from the list,
at the index given in the argument.
If nums
refers to the list [2, 3, 5, 7],
then nums.pop(1)
removes the item at index 1 (in this
example, 3), leaving the list at 2, 5, 7
. Notice how the
later elements “move up” one spot, so that
nums[1]
is now 5, and nums[2]
is now 7.
append
methodAs an example of an instance where we might use append
,
suppose we want to a program that builds up a list of all the
factors of a number entered by the user.
num = int(input())
factors = []
for k in range(1, num):
if num % k == 0:
factors.append(k) # better than "factors = factors + [k]"
Admittedly, “factors = factors + [k]
”
also works to make factors
reference a list with k
added to its end. However, that technique ends up creating a
new list, and all the old values in factors
are
copied over into the new list. This copying process is very
expensive.
By contrast, factors.append
doesn't create a
different list, but it instead grows the existing list. It is
consequently far more efficient. For this reason, when it's
appropriate, the append
method is far preferred to adding
two lists together.
(Actually, append
occassionally creates a
new list. But overall, it's still far faster.)
pop
methodNow suppose we want to remove all words starting with p from a list. We might naturally try this.
words = input().split()
for i in range(len(words)): # this does not work!
if (words[i])[0] == 'p':
words.pop(i)
print(' '.join(words))
This makes sense: We're stepping through each index, checking whether the word at that index has p as its first letter, and then removing that word if it does.
But it doesn't work. It's easiest to see this by example. Suppose the user enters “peter piper picked a peck of peppers”. Here is a brief summary of what happens.
initially: | [peter, piper, picked, a peck, of, peppers] |
i = 0 : |
[piper, picked, a peck, of, peppers] |
i = 1 : |
[piper, a peck, of, peppers] |
i = 2 : |
[piper, a of, peppers] |
i = 3 : |
[piper, a of] |
i = 4 : |
“list index out of range” |
When i
is 0, we correctly see that peter
starts with p, so we remove it. Crucially,
pop
moves everything forward one spot in the list,
so that what was second in the list is now first.
So when i
is 1, when we examine what is now in slot 1,
we'll get picked from the array, and remove it.
We continue to more values for i
.
Now, len(words)
was computed when we first reached the
for
loop, and at that time the list had seven values in it.
So as we began the loop, we committed to going through the loop
for each value from 0 up to 6. So Python will try to execute the
loop with i
being 4. However, by the time we reach 4,
the list only has three elements in it; the attempt to retrieve
words[i]
looks for something beyond the list's end.
Python terminates the program prematurely, without displaying
anything beyond the message “list index out of
range”.
So this attempt has two problems: First, it fails to remove all words starting with p as we intended; and second, it crashes so in fact the program has no result at all. What can we do to avoid this? The simplest approach is to reverse the loop to count from the end of the list forward. With this repaired, we'll never access invalid indices, and we'll ensure that all items are removed appropriately.
initially: | [peter, piper, picked, a, peck, of, peppers] |
i = 6 : |
[peter, piper, picked, a, peck, of] |
i = 5 : |
[peter, piper, picked, a, peck, of] |
i = 4 : |
[peter, piper, picked, a, of] |
i = 3 : |
[peter, piper, picked, a, of] |
i = 2 : |
[peter, piper, a, of] |
i = 1 : |
[peter, a, of] |
i = 0 : |
[a, of] |