22 June 2008

Python mutable defaults

In the short time that I've learning Python, the most annoying "feature" is Python's mutable defaults. You can define a default parameter for a variable in a function, but if the variable happens to be mutable (like a list), you get strange side effects. The book Learning Python by Mark Lutz gives a detailed explanation on pages 373-374. I will follow some of Lutz's discussion with a few of my own comments.

Suppose you write the following function.

def saver(x=[]):
... x.append(1)
... print x
...


Then you run it like so:

>>> saver([2])
[2, 1]
>>> saver()
[1]
>>> saver()
[1,1]


The third output is probably not what you wanted. You probably wanted [1]. The problem is that the default parameter is only evaluated once, when the function definition is evaluated. A common solution is to stick an if statement at the beginning of the function body.

def saver3(x=[]):
... if x is None
... x = []
... x.append(1)
... print x
...


Another solution is to use an or statement.

def saver3(x=[]):
... x = x or []
... x.append(1)
... print x
...


Both solutions give the same behavior because they force the default parameter to be evaluated for every function execution, rather than just once as with saver(). It's really confusing, isn't it? The x in if x is None evaluates the default value, but the x in x.append(1) doesn't. I can't really think of any logical way to remember this difference. I guess I just have to memorize it.

However, as Lutz notes in his book, saver2() and saver3() aren't quite the same because if you pass an empty list, saver2([]) keeps the passed list whereas saver3([]) creates a new empty list.

No comments:

Post a Comment