Avoiding Side Effects

In Python, all variables and function parameters act as references to objects. This means the same list object can be referenced in multiple ways at the same time.

Pretend you are teaching a programming class. You give your students 5 assignments and calculate their final grade by throwing out the lowest score and averaging the remaining 4.

def final_grade(scores):
    """ Throw out lowest score and average the rest """

    lowest_score = min(scores)
    scores.remove(lowest_score)  # throw out lowest score
    return sum(scores)/4.0       # average the remaining scores

Here's the problem:

student_scores = [78, 81, 85, 92, 100]

assert len(student_scores) == 5

grade = final_grade(student_scores)

assert len(student_scores) == 5


The second assert will fail. When you modify the scores parameter inside the final_grade function, you are also modifying the student_scores list in the main program. Remember that modifying a list changes all the references to that list everywhere in your program. This can lead to bugs that are very difficult to find.

Here's a modified version of the function that avoids the problem:

def final_grade(scores):
    """ Throw out lowest score and average the rest """

    scores = scores[:]          # make local copy of list
    lowest_score = min(scores)
    scores.remove(lowest_score) # throw out lowest score
    return sum(scores)/4.0      # average the remaining scores

No comments:

Post a Comment