List mutation
A list in Python is a mutable data type, which means you can change the values stored inside them.
The following code initializes a list of 5 numbers, and then changes the contents at two indices.
temps = [48.0, 30.5, 20.2, 99.0, 52.0]
temps[2] = 22.22
temps[-1] = 55.55
Just like we used bracket notation to access items in a list, we can use it to change items in a list. All the indexing rules are the same, so the code above changes the third item (20.2 -> 22.22) and the final item (52.0 -> 55.55).
Our code is subject to the same off-by-one errors, too. Trying to change an item past the final index results in an error:
temps[5] = 111.11 # 🚫 Error!
Mutation vs. re-assignment
You might think that all the data types we've seen so far are mutable, since we could write code like:
x = 5
x = 10
However, re-assignment isn't the same as mutation. That re-assignment of x
creates a brand new number, probably storing it somewhere else in computer memory, and points x
at that new number. That's different from mutation, which will change parts of an existing object in memory, keeping the name pointing at the same object in memory.
For lists, we can also do re-assignment, to point a variable at a whole new list object in memory. That looks like:
nums = [1, 2, 3]
nums = [-1, -2, -3]
Even though it appears similar, that's fundamentally different from using mutation:
nums = [1, 2, 3]
nums[0] = -1
nums[1] = -2
nums[2] = -3
It may seem like a subtle point, but once you're programming with mutable data types, your program is subject to more kinds of bugs than before. There are some languages that don't even allow mutable data types at all, to avoid those kinds of bugs.
List mutation methods
Using bracket notation is only one way to mutate a list, and it only allows us to change items at existing indices. Fortunately, there are a large number of methods that can mutate a list. A method is any function that belongs to a particular kind of object, and we call it in a slightly different way.
For example, this call appends an item to the end of a list:
temps.append(98.6)
To call a method, we first write the name of the object (temps
), then a dot, the the name of the method (append
), and the arguments in parentheses. If append
was instead a global built-in function, we'd call it like append(temps, 98.6)
. But it's not! Since the append
method is a function that belongs to list objects, we instead use dot notation whenever we want to call it on a list.
Here are some particularly useful list methods:
Method |
Description |
---|---|
|
Adds item to the end of the list. This increases list length by one. |
|
Inserts item at the specified index. This increases the list length by one and shifts all items after the specified index forward by one index. |
|
Remove the first item from the list whose value is equal to item. It raises a ValueError if there is no such item. |
|
Returns the index of the first occurrence of an item whose value is equal to item. It raises a |
|
If no index is specified, it removes the last item from the list and returns it. Otherwise, it removes the item at the specified index and returns it. |
A list of all the possible list methods is available in the Python list documentation.
Here's a program that uses all of those methods to modify a grocery list:
groceries = ["apples", "bananas"]
groceries.append("peanut butter")
groceries.insert(0, "applesauce")
groceries.insert(3, "cheerios")
groceries.remove("bananas")
bought_food = groceries.pop()
bought_food2 = groceries.pop(1)
i = groceries.index("cheerios")
bought_food3 = groceries.pop(i)
To watch for yourself how those methods mutate the original list, step through the code on PythonTutor.
Exercise: Cities
Start from this code:
cities = ['London', 'Constantinople', 'Sydney', 'Leningrad', 'Peking']
Exercise: Planets
Start from this code:
planets = ['Mercury', 'Venus', 'Earth', 'Mars',
'Jupiter', 'Saturn', 'Uranus', 'Neptune', 'Pluto']