Least Astonishment and the Mutable Default Argument in Python

Python, as you may have observed is an easy and user-friendly language. However, sometimes you may come across tricky behavior in the language that may surprise you. One such behavior is that of the Mutable Default Argument in python.
In this tutorial, you will understand Least Astonishment and the Mutable Default Argument in Python.

Least Astonishment in Python

The Principle of Least Astonishment states that ‘If a necessary feature has a high astonishment factor, it may be necessary to redesign the feature’.

In simple terms, it ideates that the behaviour of a system/software must be as expected by the users. As the name tells, the behaviour of the system must not cause any sudden astonishment(surprise).
One such behaviour is that of the mutable default arguments in Python.

Mutable Default Argument in Python

One of the common astonishment that people new to Python programming encounter is that of the mutable default arguments in python.
Observe the below example;

def foo(a=[]):
    a.append(5)
    return a
print(foo())
print(foo())
print(foo())
[5]
[5, 5]
[5, 5, 5]

Normally, programmers new to Python will expect the function foo to return a list with one element 5, regardless of the number of times you call it. However, the function doesn’t behave as expected. Instead, the list keeps getting longer with the same entity being returned each time.

So, why do you think this happens? On thinking deeply over this question, you will realise that the function uses the same object regardless of which call. You can understand this better by looking below;

def foo(a=[]):
    a.append(9)
    return a
print(foo())
print(id(foo()))
print(foo())
print(id(foo()))
print(foo())
print(id(foo()))
[9]
2091797250184
[9, 9, 9]
2091797250184
[9, 9, 9, 9, 9]
2091797250184

Basically, default arguments are checked during the execution of the def statement itself. So, whenever you pass a mutable value as a default argument in the function, python retains the default value of that entity and mutates the changes in the default too. That is, when the function modifies the object’s value, its default value is also modified in turn.

How to solve this problem?

As you can see, this problem will easily be solved if a new object is created each time the function is called. You can do this by using None to show that no argument is being provided. Further, you can assign the mutable value inside the function.
You can see the same below:

def foo(a=None):
    if a is None:
        a=[]
        a.append(9)
        return a
print(foo())
print(foo())
print(foo())
[9]
[9]
[9]

Also read, Python and the Principle of Least Astonishment.

Leave a Reply

Your email address will not be published.