Python Arena
Step 4 of 4

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)
Output
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)
Output
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))
Output
90
70

Each 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.

Your task

Challenge 1 of 3

In a diving contest, the judges' scores for a dive are in a list. The highest and the lowest score are dropped, and the dive gets the mean of the rest. dive_score(scores) returns the right score, but it also removes two scores from the caller's list. Fix it so it doesn't change the list it's given.

scores = [7, 9, 8, 5, 9]
print(dive_score(scores))
print(scores)
8.0
[7, 9, 8, 5, 9]

Challenge 2

What does this program print? Type each line it prints.

Challenge 3

Write a pure function without(items, value) that returns a new list with every item except the ones equal to value, in their original order. The list it's given must not change.

items = [1, 3, 1, 2]
print(without(items, 1))
print(items)
[3, 2]
[1, 3, 1, 2]