Python's Mutable Default Problem 7

Posted by Tim Ottinger Thu, 22 May 2008 22:16:00 GMT

Today I was perusing a very fine python article all professional python programmers should read. I had to grin when I saw one of my favorite python quirks/flaws.

Python only evaluates default parameters when it defines a function, leading to surprising side effects (example from saifoo):
def function(item, stuff = []):
    stuff.append(item)
    print stuff

function(1)
# prints '[1]'

function(2)
# prints '[1,2]' !!!

The grin on my face was because I was bit by this one again last Tuesday. You just don’t want to make a mutable object a default parameter. The solution I used was close to the solution from saifoo:

Saifoo.net:
def function(item, stuff=None):
   if stuff is None:
      stuff = []
   stuff.append(item)
   print stuff
This, of course, gives the correct behavior because it creates a new empty list each time it is called without a second parameter.

My solution actually combines some advice contained earlier in the article with the solution given for this problem:

Mine
def function(item, stuff=None):
   stuff = stuff or []
   stuff.append(item)
   print stuff

It’s not necessarily prettier, and I struggle with whether it is more obvious (to a Python programmer) or not. I fear it may be clever (a word I only use in the pejorative sense), but it is also clean-looking to me.

The point remains, though, that the mutable default parameter quirk is an ugly corner worth avoiding, and I suspect that any code that purposefully exploits that behavior to populate a structure that persists across calls will run a very real risk of being misunderstood.

We all know what happens when the next programmer to touch a program misunderstands it.

Comments

Leave a response

  1. Avatar
    paddy3118 about 3 hours later:

    You get bit once, but have you seen anyone leave it in on purpose?

    - Paddy.

  2. Avatar
    Kevin about 5 hours later:

    So is that link up at the top supposed to point back to this article? Just curious :-)

    Kevin

    Ooops. Fixed now. Thanks. —tim
  3. Avatar
    Tim about 9 hours later:

    I’ve not used it on purpose. So far as I know, I’ve not seen it used on purpose. I’ve accidentally used it a few times. The tinker in me wants to try using it on purpose, and the professional refuses. Yet another case of ambivalence.

  4. Avatar
    John Roth about 13 hours later:

    Two comments. First, the or isn’t a hack. It’s a standard Python idiom, although it looks a bit strange to someone who doesn’t have a lot of Python experience.

    Second, having a mutable object as a parameter is definitely a gotcha for most Python programmers. The interesting thing is that it could be used as a way of getting a persistent variable into a method (local in some languages). However, I’ve never seen it used that way, and I’d be a bit leery of doing it myself except in programs that are part of a project where all the developers are comfortable with the idiom, and where the code isn’t expected to leave the project.

    John Roth

  5. Avatar
    matthew about 13 hours later:

    I wonder if this was a deliberate decision, to aid with recursion?

  6. Avatar
    yachris2112 4 days later:

    Once again, I have to point out that Lisp had this first :-). I remember (way) back in the day having this explained to me by none other than David Moon. For pythonistas, this is like seeing a noob post responded to by Guido.

    Anyway, Ruby has a similar idiom:

    somevar ||= default_val

    although the ‘or’ syntax would work as well. I think the ’||’ version is a bit more noticeable.

    matthew: almost certainly not.

  7. Avatar
    JustRanting 29 days later:

    I wonder how Python and professional fit together ;-)

Comments