Common Mistake about Python default parameter value

October 1st, 2009 by Ahmed S. Farghal Leave a reply »

I know it’s a little bit long title but didn’t find a better one :) It’s one of the common mistakes a python beginner does and even more experienced programmers can fall into the same mistake so I thought it might be useful if I illustrated that out here so you can know the concept.

you can easily fall into writing code that look like that:

def get_me_bad_list(arg1, myList=[]):
    myList.append(arg1)
    return myList

In [1]: get_me_bad_list(“hello”)
Out[1]: ['hello']

In [2]: get_me_bad_list(“world”)
Out[2]: ['hello', 'world']

Interesting, no?

The issue here is that the default value for function parameter is evaluated at function definition time (i.e, module import?) and as list objects are mutable they allow you change the value so you keep having the same default object for every function call and you modify it and return it back and even worse the caller can change it and it’ll reflect the list inside the function like the following

In [3]: x = get_me_bad_list(“Assalam”)

In[4]: x.append(“Alaykum”)

In[4]: get_me_bad_list(“Ya Shabab”)

Out[4]: ['hello', 'world', 'Assalam', 'Alaykum', 'Ya Shabab']

So, what’s the right way to do that? (if you are not doing that intentionally and you just want the plain default function parameters) that you define the initial value inside the body of the function (execution time)

def get_me_good_list(arg1, myList=None):
    myList = [] if myList is None else myList
    myList.append(arg1)
    return myList

In [1]: get_me_good_list(“hello”)
Out[1]: ['hello']

In [2]: get_me_good_list(“world”)
Out[2]: ['world']

and I’ll leave the rest of the tests to you, but generally the explanation is that everytime you call the function we create a new list (if you didn’t supply one explicitly) and that list is populated and returned so you won’t see any nasty behavior.

It’s interesting that you see that the deep you understand how the python interpreter scan and execute the code can really affect the way you fall into mistakes because if you are good enough then you will be somehow safe :)

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Slashdot
  • StumbleUpon
  • DZone
  • PDF
  • Reddit
  • RSS
  • Twitter
Advertisement

8 comments

  1. A common pattern is
    def f(a=None):
    a = a or list()

    which is slightly less verbose. Do note this approach does not work in case the function behaviour would be a modification of the input list, like
    def f(a=None):
    a = a or list()
    for i in xrange(len(a)):
    a[i] = a[i] + 1
    return a

    b1 = [1, 2, 3]
    assert f(b1) is b1

    b2 = []
    # This fails
    assert f(b2) is b2

    But obviously that’s in first place extremely bad/stupid API design.

    • Yes, this makes:
      myList = [] if myList is None else myList
      better because it won’t fail in your second scenario, good point ;)

      • The ‘or’ thing comes from the days when A if PRED else B was not part of the language (it was introduced in Python 2.5 is I’m not mistaken) :-)

        Similar:
        >>> True and 1 or 2
        1
        >>> False and 1 or 2
        2

        This construct has a major catch though:
        >>> False and 0 or 1
        1
        >>> True and 0 or 1
        1

  2. it’s a great point that you show .
    but i want to understand this piece of code
    myList = [] if myList is None else myList
    can you illustrate it , please ?

    • it’s like ternary operator in C of Java, myList will be assigned a new empty list ( [] ) if the passed myList was set to None, but if it has a value it’ll be pointing to that value.

    • It’s pretty much like this construct in C/C++/Java/…:
      myList = !myList ? [] : myList, or the inverse: myList = myList ? myList : []

      Or:

      if myList is None:
      myList = []
      # No need for an else case, this would be "myList = myList" anyway

      I think the Python-form is slightly better to read if you put the ‘most common case’ as truth value of the predicate. This makes sure the ‘most common value’ will be immediately visible to the reader, on the right of the assignment operator.

  3. amal says:

    thanks
    I faced the same problem before
    and it took time to understand the problem

  4. Joni Wilmoth says:

    This is great! Thanks for this post. I am starting development and this got me straight.

Leave a Reply