Functions that mutate
A function takes one or more parameters. Some functions will mutate or change their parameters. For example, a function may take a list as a parameter, and then append or remove things from the list, or change items in the list.
In general, when you write a function, it is a good idea to avoid mutating the parameters you are given. Let’s walk through a few examples
Swapping the first and last items in a list
Here is a function that mutates a list — it swaps the first and list items:
def swap_first_and_last(items):
"""Swap the first and last items in the input list. Modify the list in place."""
first = items[0]
items[0] = items[-1]
items[-1] = first
numbers = [1, 2, 3, 4, 5]
swap_first_and_last(numbers)
print(numbers)
This will print:
[5, 2, 3, 4, 1]
It is generally preferred to write functions that do NOT mutate their inputs. Whenever possible, try to write functions that don’t mutate.
A version that does not mutate the list
For example, this code will also swap the first and last items in a list, but it returns a new list instead of modifying the original list.
def swap_first_and_last(items):
"""Swap the first and last items in the input list. Return a new list."""
new_items = []
# append the last item
new_items.append(items[-1])
# append the middle items
for index in range(1, len(items) - 1):
new_items.append(items[index])
# append the last item
new_items.append(items[0])
return new_items
numbers = [1, 2, 3, 4, 5]
new_numbers = swap_first_and_last(numbers)
print(new_numbers)
Be sure to document whether your function mutates its inputs or not. Write non-mutating functions whenever possible.
Reversing a list
Python has a built-in function for reversing a list that mutates the list:
numbers = [3, 4, 10, 7, 1]
numbers.reverse()
print(numbers)
This will print:
[1, 7, 10, 4, 3]
Let’s write a version that does not mutate the list:
def reverse_list(items):
"reverse the list, and return a NEW list with the inverted copy"
new_list = []
for i in range(len(items)):
index = -1 - i
new_list.append(items[index])
return new_list
numbers = [1, 7, 10, 4, 3]
new_list = reverse_list(numbers)
print(new_list)
To reverse the list we need to start with item -1
— the last item in the list
— and then continue with item -2
, -3
, etc. The index
variable will
iterate through this list of numbers, by subtracting i
from -1
.
So the first time through this loop, we will append item items[-1]
to
new_list
. The second time we will append items[-2]
. And so forth.
Here is a different way to do the same thing:
def invert(items):
"invert the list, and return a NEW list with the inverted copy"
new_list = []
end = -(len(items) + 1)
for i in range(-1, end, -1):
new_list.append(items[i])
return new_list
The value len(items)
is 5
, so len(items) - 1
equals 6
. This means end
equals -6
.
Given end
, the range()
counts from -1
down to -6
, subtracting 1
each
time, so it counts: -1, -2, -3, -4, -5
. We can then use i
to get the item we
need from items
and append it to new_list
.
Just like above, we will append item items[-1]
to new_list
. The second time
we will append items[-2]
. And so forth.