Python’s mutable default arguments in the function definition and it’s side effects.

What is the output of this python code?

def myList(val, list=[]):
    list.append(val)
    return list


list1 = myList('python')
list2 = myList(155, [])
list3 = myList('abc')


print('list1 is %s' % list1)
print('list2 is %s' % list2 )
print('list3 is %s' % list3)

The output. Why?

list1 is ['python', 'abc']   
list2 is [155]
list3 is ['python', 'abc']

For the benefit of those that are new to programming, I thought it would help to explain the code in a little bit more detail.

In the code above we have a function definition declared using the keyword ‘def ‘. The name of the function is, ‘myList’. The function has two parameters surrounded by open and closed parentheses, and delimited by a comma. The first parameter is a variable named ” val “. The function’s second parameter is the default parameter and we know this because of the use of an assignment operator, the ( = ) symbol. The assignment operator is used here to assign the empty list ( [ ] ) , on the right side of the operator, ( a mutable object: meaning we can change values inside the list) to the variable on the left side, named ‘ list ‘. The default parameter is an empty list in this function’s definition: the list contains no values. So, if the function were to be called without a second argument value, (as demonstrated by list1 and list3 in the code) in the call, then the function will use it’s own ‘default’ value. In our example, that default being an empty list.

Things to Remember: The default parameter here is a ‘mutable’ Python object- a list[ ]. Behaviors are different for immutable objects. The function ‘myList’ is defined by the ‘keyword’ def in Python. The keyword ‘def ‘ is an executable. When the python module runs ‘def’ it executes and the calculation of expressions (the default argument: the list=[ ]) occurs at this time. That means the default ‘list’ is created once in this function’s namespace. Meaning, the same defined parameter object get used whenever the function is called. The same ‘list’ is used! So, if the function gets called without a ‘list’ argument, as demonstrated by list1 and list3 shown in the code above, the function’s default ‘list’ parameter is used. But what happens if a list argument is given? Any successive function calls passing a mutable argument, such as a ‘list’, like in the example code, list2 in the scope of the caller, it uses that new list argument object.

# A work-around; there are many.

def myList(val, list=None):
    if not list: list = []
    list.append(val)
    return list

# another way to demonstrate this is to:

default_list=[]

def exList(val, dflist=default_list):
    dflist.append(val)
    return dflist

# and yet another way:

def lastList(val, list=None):
    if list is None:
        list = []
    list.append(val)
    return list 
    

Take-aways:

Consider the functions default’s as ‘static’. They are a constant reference pointing to ONE object and stored in the function definition, and then evaluated at method definition time as part of the function itself.