Constructor options reused for new object?

I am new to Python and, using it to write a small application, ran into a problem. Since I cannot find anything like this on Google, I may have missed some basic Python concepts. It would be great if one of you guys could explain this to me.

So here is a minimal working example. Please ignore class and variable names, as said, this is just an artificial example, but it shows what I mean. You can c & p this in your IDE and run it for yourself.

class State(object): def __init__(self, wrappers = []): self.wrappers = wrappers def add(self, name, items): lst = KeyValList(name) for item in items: lst.add(item, items[item]) self.wrappers.append(Wrapper(lst)) class Wrapper(object): def __init__(self, kv_lst): self.kv_lst = kv_lst class KeyValList(object): def __init__(self, name, kvals = []): self.name = str(name) self.kvals = kvals def add(self, key, value): self.kvals.append(KeyVal(key, value)) class KeyVal(object): def __init__(self, key, val): self.key = str(key) self.val = val 

Ok, this is the definition of the classes that I use.
Below I write the following:

 state = State() kv = { "key1" : "1", "key2" : "2", "key3" : "3" } state.add("1st", kv) kv = { "keyX" : "X", "keyY" : "Y", "keyZ" : "Z" } state.add("2nd", kv) for wrap in state.wrappers: print wrap.kv_lst.name, "-->", for kv in wrap.kv_lst.kvals: print "%s:%s" % (kv.key, kv.val), print "" 

As a Java programmer, I expect the following output:

 1st --> key3:3 key2:2 key1:1 2nd --> keyZ:Z keyY:Y keyX:X 

However, with this program in Python, I get the following output:

 1st --> key3:3 key2:2 key1:1 keyZ:Z keyY:Y keyX:X 2nd --> key3:3 key2:2 key1:1 keyZ:Z keyY:Y keyX:X 

Why do both lists contain all key / value pairs?

I expect a problem somewhere in the KeyValList class, because during debugging, when the state.add() method is called in the main program a second time, the kvals parameter of the kvals constructor contains the first pairs. but how can it be? I do not provide anything to this kvals parameter, should it be initialized with an empty list due to the default values ​​for the parameters?

PS: I am using Python 2.7.3 for Windows.

+4
source share
2 answers

Instead of using mutable default arguments, use None as a placeholder and your functions / methods create new containers with every call.

Change this:

 class State(object): def __init__(self, wrappers = []): self.wrappers = wrappers 

For this:

 class State(object): def __init__(self, wrappers = None): if wrappers is None: wrappers = [] self.wrappers = wrappers 

There is a good explanation of the problem in this blogpost .

+1
source

This is basically an instance of a very common and, rather, intuitive, Python function, a mutable default argument . The bottom line is that the default arguments are properties of the function object, and thus, if you mutate them in one function call, you mutate them for all function calls.

This example is a bit more complicated than the standard default argument variable, as you also pass the same object as an attribute and an instance of the class. But in your code there is only one list object - the one created by [] - and therefore mutates it through an instance attribute, the same as mutating it on the function object.

In other words, when you write

 self.kvals.append(...) 

you add some data to a specific list object. What is a list object? The one that was created [] in the function definition and thus the same that Python uses as the default argument to the function.

+2
source

All Articles