0

Possible Duplicate:
“Least Astonishment” in Python: The Mutable Default Argument

Using python 2.7 I came across strange behaviour and I'm not sure how to explain it or if it even exists in any python docs. Using the code

class MyClass:
@staticmethod
def func(objects=[], a=None, b=None):
    objects.append(a)
    print 'objects: %s'%objects
    print 'b: %s'%b


MyClass.func(a='one')
MyClass.func(a='two', b='foo')
MyClass.func(a='three')

I get the get output

objects: ['one']
b: None
objects: ['one', 'two']
b: foo
objects: ['one', 'two', 'three']
b: None

As you can see, the first list parameter (objects) of the method retains it's values across calls.. new values being appended to the last list even though in it's header declaration it has a default value of []. But the last parameter (b) does not retain it's value, it is reset to the default value between calls.

The expected (for me anyway) is that the objects parameter should be reset to it's default on any call to the method (like the b parameter is), but this doesn't seem to happen and only seems to occur on the first call.

Can anyone explain this behaviour? Is it a bug in this version of python or is it intended behaviour? Possibly something to do with the list reference being retained across calls but the string variable (b) is not? I'm very confused by this behaviour.

Thanks

Community
  • 1
  • 1
Jason N
  • 463
  • 3
  • 10
  • 1
    http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument – mayhewr Apr 20 '12 at 00:31
  • Thanks, that answered my question. Good link that explains it all from the answer at that link http://effbot.org/zone/default-values.htm – Jason N Apr 20 '12 at 00:56

2 Answers2

3

It has nothing to do with being an staticmethod. This is a very common mistake in Python.

Functions in Python are first class objects, not just a block of code, so the empty list you assign to objects in the parameters is a real list attached to the function object. Everytime you call it and append something to that list, you are using the same list. It's easy to see it happening this way:

>>> def func(x, objects=[]):
...     objects.append(x)
... 
>>> func(1)
>>> func.func_defaults
([1],)
>>> func(2)
>>> func.func_defaults
([1, 2],)
>>> func(3)
>>> func.func_defaults
([1, 2, 3],)

func_defaults is the function object attribute that constains the default keyword parameters you set. See how the list is there and get changed?

The proper way to do what you want is:

class MyClass:
@staticmethod
def func(objects=None, a=None, b=None):
    if objects is None:
        objects = []
    objects.append(a)
    print 'objects: %s'%objects
    print 'b: %s'%b
Pedro Werneck
  • 40,902
  • 7
  • 64
  • 85
  • I've never seen func_defaults before. Is that documented anywhere as part of the python API? – mgilson Apr 20 '12 at 01:22
  • It probably isn't documented anywhere, like most of the internal implementation details. It really shouldn't be used for anything serious, and other Python implementations might not conform to it. – Pedro Werneck Apr 20 '12 at 01:31
  • I was wondering if it was an implementation detail...Still very interesting. – mgilson Apr 20 '12 at 01:39
1

This behaviour is broadly known and by many treated as a feature, and is not staticmethod-specific. It works for every function. It happens when you assign mutable object as the default value to the argument.

See this answer on StackOverflow: Default Argument Gotchas / Dangers of Mutable Default arguments

If you understand mutability vs. immutability issue, think of the function / method like that:

  • when it is defined, the default argument values are assigned,
  • when it is invoked, the body is executed and if it changes mutable default argument value in-place, the changed value is then being used on every subsequent call where the default argument value has not been overriden by providing different value instead of it,
Community
  • 1
  • 1
Tadeck
  • 132,510
  • 28
  • 152
  • 198