Computer Science

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.