Anti Pattern - Mutable Default Arguments

dollardhingra

Dollar Dhingra

Posted on October 31, 2021

Anti Pattern - Mutable Default Arguments

Introduction

In this article, you will understand what mutable default arguments are, why you should avoid using them and the workaround.

Code Example With A Mutable Default Argument

Consider the following code example below.

def add_fruit(fruit, box=[]):
    box.append(fruit)
    return box

Enter fullscreen mode Exit fullscreen mode

Let's understand step by step what is happening:

  • We are creating a function to add fruits(str) in a box(list)
  • There is a add_fruit function which is responsible for adding the fruit
  • This function takes 2 arguments: fruit and box
  • Attention! : The second argument here is a mutable default argument.

So, what is mutable default argument?

An argument in a function with default value as mutable.

In short, Python has both mutable and immutable types. The difference is:

  • mutables can be modified
  • immutables can't be modified.

For eg: Tuple is an immutable type. If we define a tuple like this:

weekends = ('saturday', 'sunday',)

weekends[0] = 'Monday' # TypeError: 'tuple' object does not support item assignment
Enter fullscreen mode Exit fullscreen mode

An immutable type canot be modified.

You Might Expect

let's modify our code and create a couple of boxes, i.e. red box and yellow box

def add_fruit(fruit, box=[]):
    box.append(fruit)
    return box

red_box = add_fruit("apple")
print(f"red box: {red_box}")

yellow_box = add_fruit("mango")
print(f"yellow box: {yellow_box}")
Enter fullscreen mode Exit fullscreen mode

Expected Output

red box: ["apple"]

yellow box: ["mango"]
Enter fullscreen mode Exit fullscreen mode

Actually Output

Actually, you get the following output:

red box: ["apple"]

yellow box: ["apple", "mango"]
Enter fullscreen mode Exit fullscreen mode

Wait? What? We never added apple in the yellow box.

What Exactly Happened?

A new list is created once when the function is defined, and the same list is used in each successive call.

Python’s default arguments are evaluated once when the function is defined. This means that if you use a mutable default argument and mutate it, you will and have mutated that object for all future calls to the function as well.

We will get the same result for other mutable types also(For eg: dict).

What Should Be Done?

If your function needs to have a default argument for a mutable type, then default it with None and also add a check for the same.
Let's modify our add_fruit function:

def add_fruit(fruit, box=None):
    if box is None:
        box = []

    box.append(fruit)
    return box

red_box = add_fruit("apple")
print(f"red box: {red_box}")

yellow_box = add_fruit("mango")
print(f"yellow box: {yellow_box}")
Enter fullscreen mode Exit fullscreen mode

This extra check can saves hours of debugging!

Conclusion

It's always a best practice to not use mutable default arguments. Instead, try adding an extra comparison check with
None to handle the default arguments which are mutable.

💖 💪 🙅 🚩
dollardhingra
Dollar Dhingra

Posted on October 31, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related