18

Can someone point out to me what I'm doing wrong or where my understanding is wrong?

To me, it seems like the code below which instantiates two objects should have separate data for each instantiation.

class Node:
    def __init__(self, data = []):
        self.data = data

def main():
    a = Node()
    a.data.append('a-data') #only append data to the a instance

    b = Node() #shouldn't this be empty?

    #a data is as expected
    print('number of items in a:', len(a.data))
    for item in a.data:
        print(item)

    #b data includes the data from a
    print('number of items in b:', len(b.data))
    for item in b.data:
        print(item)

if __name__ == '__main__':
    main()

However, the second object is created with the data from the first:

>>> 
number of items in a: 1
a-data
number of items in b: 1
a-data
KobeJohn
  • 7,390
  • 6
  • 41
  • 62
  • 1
    Duplicate: http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument, and http://stackoverflow.com/questions/2313075/python-default-value-for-a-function, and numerous others. Anything having do with "mutable" and "default". – S.Lott Jul 01 '10 at 21:34
  • 1
    Thanks all for the quick answers. I tried hard to find this issue before posting the question but was not able to. Hopefully the title of this question will help others to google it from the perspective of someone who doesn't understand that the issue is the mutable object in the function definition. – KobeJohn Jul 01 '10 at 21:43

3 Answers3

16

You can't use an mutable object as a default value. All objects will share the same mutable object.

Do this.

class Node:
    def __init__(self, data = None):
        self.data = data if data is not None else []

When you create the class definition, it creates the [] list object. Every time you create an instance of the class it uses the same list object as a default value.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • Aaah. I have been modifying the class definitions in all my programs then. Thank you very much for the clear and succinct answer. – KobeJohn Jul 01 '10 at 21:37
  • I prefer this syntax: self.data = data or [] – Matthew J Morrison Jul 01 '10 at 22:04
  • 2
    @Matthew J Morrison: not the same thing, pass something false-ish to data and it breaks. – Jochen Ritzel Jul 01 '10 at 22:51
  • @THC4k, that is true, I won't argue that. – Matthew J Morrison Jul 01 '10 at 23:20
  • This sure sounds like a bug because I can't imagine Python creators having a dialogue like "Hey, remember we added support for default args? Let's now make it so when one is a list, it gonna be magically stored globally over all class instances, just to confuse people and make debugging more annoying". – Hi-Angel Jul 10 '20 at 15:05
6

The problem is in this line:

def __init__(self, data = []):

When you write data = [] to set an empty list as the default value for that argument, Python only creates a list once and uses the same list for every time the method is called without an explicit data argument. In your case, that happens in the creation of both a and b, since you don't give an explicit list to either constructor, so both a and b are using the same object as their data list. Any changes you make to one will be reflected in the other.

To fix this, I'd suggest replacing the first line of the constructor with

def __init__(self, data=None):
    if data is None:
        data = []
David Z
  • 128,184
  • 27
  • 255
  • 279
3

When providing default values for a function or method, you generally want to provide immutable objects. If you provide an empty list or an empty dictionary, you'll end up with all calls to that function or method sharing the object.

A good workaround is:

def __init__(self, data = None):
    if data == None:
        self.data = []
    else:
        self.data = data
Wilduck
  • 13,822
  • 10
  • 58
  • 90