Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python adding element to an instance's list also adds it to another instance's list [duplicate]

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

I've created a Person class which has a name and a list of children. The children are of the same Person class. If I create two Person instances, and add a child to one of the person instances, it gets added to both Person instances. I would assume the child should only be added to the children of person1 and not to person2, but it gets added to both. I must be doing something obvious wrong, but I can't see it.

Similar questions showed people adding the variable definition to the class itself instead of the constructor, but that's not what's happening here. Any help would be much appreciated, it's frying my brain!

See below for code and output:

class Person(object):

    def __init__(self, name, children=[]):
        self.name = name
        self.children = children

    def __repr__(self):
        return '<' + self.name + '>'

    def add_child(self, child):
        self.children.append(child)


def main():
    person1 = Person('Person1')
    person2 = Person('Person2')

    person1.add_child(Person("Person 1's child"))

    print "Person 1's children:", person1.children
    print "Person 2's children:", person2.children

if __name__ == "__main__":
    main()

output of this code is:

Person 1's children: [<Person 1's child>]
Person 2's children: [<Person 1's child>]
like image 534
zeefox Avatar asked Jan 25 '13 13:01

zeefox


2 Answers

The problem is in the default value of the children parameter. If you use a mutable object (such as list) and initialize it in the header, it will be initialized just once and then used in all instances.

This has been discussed here.

Try to replace

def __init__(self, name, children=[]):
    self.name = name
    self.children = children

with

def __init__(self, name, children=None):
    self.name = name
    if children is None:
        self.children = []
    else:
        self.children = children
like image 173
eumiro Avatar answered Nov 14 '22 21:11

eumiro


For your init, use this instead:

def __init__(self, name, children=None):
    self.name = name
    if children is None:
        self.children = []
    else:
        self.children = children

What's happening is the two Person objects are using the same list in the background (the list gets bound when the function is defined, not when it's executed). Take a look at this question for a bit more discussion.

like image 43
thegrinner Avatar answered Nov 14 '22 23:11

thegrinner