Don’t use default mutable arguments!

What the heck is that?

default - just as it says: default

mutable - a data type whose reference value can be changed without changing the pointer

arguments - the values passed in to a method or function

Consider the following code:

def example(the_devil = []):
	return the_devil

assert example() == []
assert example([1]) == [1]

This looks okay, right?

WRONG!!!!!!

As you can see, the Example 1 specifies a default value for the parameter: the_devil.

Consider the following code:

def oh_no_dont_do_it(your_heart = []):
	your_heart.append(1)
	return your_heart

but_i_did = oh_no_dont_do_it()
assert but_i_did == [1]

# Now let's update the value for our pointer
but_i_did.append(9001)
assert but_i_did == [1, 9001]

# Okay... let's do it again
oops_i_did_it_again = oh_no_dont_do_it()
assert oops_i_did_it_again == [1, 9001, 1]  # Because this makes total sense

Uh, What?????

Okay, so... Let’s take a look as to why this happened... First we should rewrite this function to leverage type hints and more sensible naming.

from typing import List

def append_one(arg: List = []) -> List[int]:
	arg.append(1)
	return arg

first = append_one()
second = append_one()

assert first == [1, 1]
assert second == [1, 1]
assert first == second

Okay, if we look at the List type documentation we can see that it inherits from MutableSequence. Mutable... 😈

In other words, we have specified the default value of our argument to be a child of MutableSequence.

What do I do now?

Let’s fix this by assigning an immutable value as the default argument and then overwrite the pointer inside of the closure when we execute the function.