Mutable Default Arguments

When you mix mutable arguments with default arguments in Python, it can lead to some unexpected results.
Let's illustrate this with a simple example. Suppose you are a teacher and you want to create a function to record student scores for different assignments. You want to use a list to keep track of these scores. So, you might initially write something like this:
def record_score(new_score, score_list=[]):
    score_list.append(new_score)
    return score_list

print(record_score(95))  # [95]
print(record_score(85))  # [95, 85]
This seems like it would work fine. The function record_score takes in a score and a list. If no list is provided, it uses an empty one (the score_list=[] in the function definition).
When you call record_score(95), it looks like the function is working correctly - it returns a list with a score of 95. But when you then call record_score(85), the function returns a list with both 95 and 85. It's as if the function remembered the list from the last time it was called.
This might make you scratch your head. In most programming languages, when a function ends, all of its local variables (like score_list) are thrown away and forgotten. The next time the function is called, it starts fresh.
But in Python, the behavior is different when a mutable object is used as a default argument. Python only creates the default argument once when the function is defined. So every time you call record_score without providing a list, Python uses the same list it created the first time the function was defined. If you modify that list (by appending a score), the modification remains the next time the function is called.
So, in our function, the score_list is shared across all function calls where we don't supply our list. This is why the scores are stacking up in our example.
This is not what we wanted! We wanted each call to record_score to start with an empty list unless we provided our own list.
To avoid this, a common practice is to use None as the default value for arguments that could be mutable. Then, inside the function, you can create a new mutable object if the argument is None. Here's how you could write the record_score function to avoid the problem:
def record_score(new_score, score_list=None):
    if score_list is None:  # If no list was provided,
        score_list = []     # create a new one
    score_list.append(new_score)
    return score_list

print(record_score(95))  # [95]
print(record_score(85))  # [85]
In this corrected version, each call to record_score that doesn't provide its own list gets a brand-new list. This way, the scores aren't shared across function calls.
💡
So, the key takeaway is this: Mutable default arguments in Python can lead to unexpected behavior because they are created only once, and the same object is used every time the function is called. This might not be what you want, especially when you modify the mutable object. To avoid this problem, you can use None as the default value and create a new object in the function if needed.
 
To check your solution you need to sign in
Sign in to continue