Mutable Default Parameters - Python

Mutable Default Parameters - Python

Introduction

Passing in a default parameter like a list may lead to unexpected behavior from your program. In this article, we are going to see what mutable default parameters are and how to avoid some of the undesired consequences of using them.

What is a Mutable Parameter

A mutable parameter is a parameter that can be changed or altered whenever your function is called. Some of the mutable parameters you can pass in include list, dict, set, etc.

To paint a picture of what we are trying to explain here, let's create a function that takes in one of the mutable objects we listed above as a default.

def fruit_basket(fruit, basket = []):
     # add fruit to basket
     basket.append(fruit)
     return basket

As you've seen in the fruit_basket() function we have defined above, this function takes in an argument fruit and appends it to basket - which is passed in as a list by default.

Now let's go ahead and call this function and store its output in a variable.

>>> first_fruit = fruit_basket('orange')
>>> print(first_fruit)
    ['orange']

We got exactly what we expected from the function, it returned a list with orange in it. Let's do another fruit and store it in a different variable this time.

>>> second_fruit = fruit_basket('apple')
>>> print(second_fruit)
    ['orange', 'apple']

Something weird happened here. second_fruit was expected to have a list with only 'apple' in it, but got 'orange' in it as well. Why is that? This happens because the fruit_basket function was evaluated once and assigned a list that every variable points to. That is why second_fruit returns a list with two elements in it. Now, let's print first_fruit to see if it still remains the same.

>>> print(first_fruit)
    ['orange', 'apple']

first_fruit now also has a list that contains two elements. This was surprising because we were expecting each variable to hold a unique list, instead. This is why default mutable parameters can be very dangerous. They essentially share state and alter the mutable parameter every time the function is called, instead of assigning each function a new list.

How Do We Avoid This Behavior

We are going to tweak the fruit_basket function a little bit to avoid this problem. We will pass in None as the default parameter.

def fruit_basket(fruit, basket = None):
    if basket is None:
        basket = [] # assign a new list
     basket.append(fruit)
     return basket

The quick fix is very simple and straightforward. We pass in None as the default parameter, then whenever the function is called, we check if basket is None, if it is, then we generate an empty list for basket and append fruit to it.

Let's try what we did before to see if we still get the same output.

>>> first_fruit = fruit_basket('orange')
>>> print(first_fruit)
    ['orange']
>>> second_fruit = fruit_basket('apple')
>>> print(second_fruit)
    ['apple']

As you can see, that fixes our problem. Now our function returns a different list whenever it's called.

Summary

We have seen what mutable default parameters are and some of the problems they can lead to and how to avoid those problems.