Wednesday, October 15, 2008

Beware Mutable default value initializers in Python

As a new Python programmer I was surprised by the behavior of default parameter expressions. I knew they were only executed once when the function is first defined, but I hadn't realized the ramifications for using a static dictionary object as a default expression. This little gotcha hit me yesterday and took quite a while for me to figure out what was happening. Here's some sample code:

def Bad(dict={}):
    print "Bad: %s" % repr(dict)
    dict['p'] = 1

Bad()
Bad()

Which results in:

Bad: {}
Bad: {'p': 1}

Since the value of dict is mutable, it can be changed as a side effect of the function. So all subsequent calls will use a default value that has been modified by previous calls to the function! This can be (and was) a nightmare to track down in a large program.

I fixed this by changing to the following:

def Good(dict=None):
    if dict == None:
        dict = {}
    print "Good: %s" % repr(dict)
    dict['p'] = 1

Good()
Good()

Which results in:

Good: {}
Good: {}

The fix de-couples the effects of one call on subsequent calls (as was the intention of the original code).