loading...

A tricky Python default argument

jotafeldmann profile image Jota Feldmann ・Updated on ・1 min read

Check the following code:

def append(l=[]):
  l.append(1)
  return l

In my opinion, we have a problematic mutator function, but for the purpose of this article, just focus on the default argument/parameter. What's wrong?

def append(l=[]):
  l.append(1)
  return l

print(append())
# [1]
print(append())
# [1,1]

Here's the tricky default parameter: any object (list, map, etc) will be instantiate and will live in the memory for the rest of the function's life!

IMHO it's problematic behavior, different from any other language, like Java or JavaScript. Ok, it's cool when you can work with dependency injection because once set, it will never be instantiated again. But in common use, you can forget that default parameter and have practical side-effects. Example: in unitary tests (my reason to write that article).

To avoid that stuff, you can check and instantiate every time, at the beginning of the function:

def immutableAppend(l=None):
  l = [] if l is None else l
  l.append(1)
  return l

print(immutableAppend())
# [1]
print(immutableAppend())
# [1]

This gotcha leads me to the great Python Guide, which first gotcha is exactly this behavior. And one more reason to read all Python doc, again 😞 (search for "Important warning: The default value is evaluated only once").

You can test it on https://repl.it/@jotafeldmann/trickyDefaultArgument

Discussion

pic
Editor guide