# List methods

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 257. Notice how the later elements “move up” one spot, so that nums[1] is now 5, and nums[2] is now 7.

## The append method

As 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(1num):
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.)

## The pop method

Now 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]