Side effects
A function that changes something outside itself, besides returning a value, has a side effect. When that is its job, as with add_bonus in the previous step, the side effect is what you want. When it happens by accident, it's a bug, and a sneaky one: the function returns the right answer, and the damage shows up somewhere else in the program.
This function works out what an order costs, with the delivery fee added:
def order_total(prices: list, delivery: int) -> int:
prices.append(delivery)
return sum(prices)
basket = [12, 30, 8]
print("To pay:", order_total(basket, 5))
print("Basket:", basket)
To pay: 55
Basket: [12, 30, 8, 5]The total is right, but append added the fee to the caller's list, and now the basket holds a 5 that the customer never chose. The function doesn't need to change the list at all to find its answer:
def order_total(prices: list, delivery: int) -> int:
return sum(prices) + delivery
basket = [12, 30, 8]
print("To pay:", order_total(basket, 5))
print("Basket:", basket)
To pay: 55
Basket: [12, 30, 8]When a function does need to change the items to find its answer, to sort them or to take some out, it should change a copy, not the list it was given.
The same call, a different answer
A side effect can also make a function give a different answer each time it's called:
def sum_except_first(numbers: list) -> int:
numbers.pop(0)
return sum(numbers)
bills = [100, 20, 30, 40]
print(sum_except_first(bills))
print(sum_except_first(bills))
90
70Each call removes an item from bills for good. A slice is a new list, so return sum(numbers[1:]) gives the same answer without touching the caller's list.
Pure functions
A function whose only result is the value it returns, and which changes nothing else, is called a pure function. Pure functions are easier to trust and to test: the same arguments always give the same answer, and calling one can't break anything elsewhere. A good habit:
- A function that computes an answer should not change its arguments. If it needs to sort, remove or replace items, it works on a copy (
sorted,copy(), a slice) or on a new list of its own. - A function whose job is to change a list says so in its description, and returns nothing.
The methods that change a list are the ones to watch in a function that computes an answer: append, insert, remove, pop, sort, and assigning to an index or a slice.