When mutability matters in Python

Concatenating strings within iterations, such as programmatically building html output:

The following code attempts to concatenate a string within a for loop, so that a HTML formatted list can be generated

beatles = ['John', 'Paul', 'Ringo', 'George', 'Bert' ... ]

html = '<ul>'

for name in beatles:
    html += '\t<li>{}</li>\n'.format(name)

html += '</ul>'


Unlike in Java or Ruby, Strings in Python are immutable, this means that for every assignment operation of the html variable above, a new memory location will be created to store each newly created value. So very quickly (particularly in production where large or dynamic datasets could exist) its easy to see how memory usage to get out of control and cause unexpected results.

lucky for us, the beatles had a hiring freeze after ringo.

Mutable objects as default arguments

def foo(val, arr=[]):
    return arr


>>> print(foo('yo'))

>>> print(foo('yo'))
['yo', 'yo']


As with most languages, Lists are mutable type in Python. This means that their value can change but their memory address will stay fixed from the point of its creation.

The gotcha with the code above is that it can easily appear as though the parameter arr is set to an empty List by default everytime the function is called without any arguments.

This is true initially as the value of arr is assigned as an empty list at the point foo is compiled, However becuase the list arr is a mutable object, it is possible for its value (and hence the parameter default) to change during runtime. This can lead to unanticipated results across function calls, and therefore should be avoided at all costs.


Its safe to use immutable object types as default paramters, as they new memory locations are created for each new value, leaving memory location for the initial parameter default unchanged.

However for mutable objects, its best to assign these within the body of the function.