In Python, why does .append give different results than using +?
Question:
I want to implement a very simple tree data structure in Python.
I want to make it so that everytime I add a new node and I specify its parent, then it is automatically added to the children attribute of its parent.
I have two different ways to do it, one works and one doesn’t but I don’t understand why.
class Node():
def __init__(self, value, parent = None, children = []):
self.value = value #This is for us to see the name
self.parent = parent #Parent node
self.children = children #List of child nodes
#Set this Node as a children of its own parent
if not parent == None:
#self.parent.children.append(self) <--- wrong code
self.parent.children = self.parent.children + [self]
def __repr__(self):
return str(self.value)
tree = Node("tree")
branch1 = Node("branch1", parent = tree)
branch2 = Node("branch2", parent = tree)
leaf = Node("leaf", parent = branch1)
Here is what I get with the code as is and what I would get if I replace the last line of __init__
with the commentated line.
print(tree.children)
#[branch1, branch2] <--- expected
#[branch1, branch2, leaf] <--- with wrong code
print(branch1.children)
#[leaf] <--- expected
#[branch1, branch2, leaf] <--- with wrong code
Using the .append
method adds the node not only to the list children
of its parent but to everybody. Even if I define a new Node("other")
completely detached from the others. Why is that?
Answers:
The problem is in the use of mutable default value:
def __init__(self, value, parent = None, children = []):
The empty list []
gets created only once, when the function is defined, and all invocations share the same list! This is why append
to one list of children modifies all of them – because they all are one and the same list object. When you use +
to append to the list, you work around the above bug because you re-create the list every time, thus unsharing the children objects.
The correct solution is to replace children=[]
with something like:
def __init__(self, value, parent=None, children=None):
if children is None:
children = []
That will guarantee the creation of a new list for children, and then append
and +
should have the same result.
I want to implement a very simple tree data structure in Python.
I want to make it so that everytime I add a new node and I specify its parent, then it is automatically added to the children attribute of its parent.
I have two different ways to do it, one works and one doesn’t but I don’t understand why.
class Node():
def __init__(self, value, parent = None, children = []):
self.value = value #This is for us to see the name
self.parent = parent #Parent node
self.children = children #List of child nodes
#Set this Node as a children of its own parent
if not parent == None:
#self.parent.children.append(self) <--- wrong code
self.parent.children = self.parent.children + [self]
def __repr__(self):
return str(self.value)
tree = Node("tree")
branch1 = Node("branch1", parent = tree)
branch2 = Node("branch2", parent = tree)
leaf = Node("leaf", parent = branch1)
Here is what I get with the code as is and what I would get if I replace the last line of __init__
with the commentated line.
print(tree.children)
#[branch1, branch2] <--- expected
#[branch1, branch2, leaf] <--- with wrong code
print(branch1.children)
#[leaf] <--- expected
#[branch1, branch2, leaf] <--- with wrong code
Using the .append
method adds the node not only to the list children
of its parent but to everybody. Even if I define a new Node("other")
completely detached from the others. Why is that?
The problem is in the use of mutable default value:
def __init__(self, value, parent = None, children = []):
The empty list []
gets created only once, when the function is defined, and all invocations share the same list! This is why append
to one list of children modifies all of them – because they all are one and the same list object. When you use +
to append to the list, you work around the above bug because you re-create the list every time, thus unsharing the children objects.
The correct solution is to replace children=[]
with something like:
def __init__(self, value, parent=None, children=None):
if children is None:
children = []
That will guarantee the creation of a new list for children, and then append
and +
should have the same result.