While writing some scripts in python, I wrote a simple class tree structure. After testing it, I got a strange but interesting result;
Suppose that we implement a simple tree such that each node has one child. Then, we may be able to implement it like this:
class node_1(object):
def __init__(self, data = 0, child = None):
self.data = data
self.child = child
def append(self, child):
self.child = child
def __str__(self):
return '(box %s)' % str(self.data)
def __repr__(self):
return self.__str__()
When we execute the following code:
root_1 = node_1(0)
root_1.append(node_1(1))
root_1.child.append(node_1(2))
print root_1, root_1.child, root_1.child.child
we get the following result, which we expected:
(box 0) (box 1) (box 2)
Now let's suppose that each node can have multiple childs; if we implement it like the following code:
class node_2(object):
def __init__(self, data = 0, childs = []):
self.data = data
self.childs = childs
def append(self, *childs):
map(self.childs.append, childs)
def __str__(self):
return '(box %s)' % str(self.data)
def __repr__(self):
return self.__str__()
Then the code
root_2 = node_2(0)
root_2.append(node_2(1), node_2(2))
root_2.childs[0].append(node_2(3), node_2(4))
print root_2, root_2.childs, root_2.childs[0].childs
outputs
(box 0) [(box 1), (box 2), (box 3), (box 4)] [(box 1), (box 2), (box 3), (box 4)]
, instead of
(box 0) [(box 1), (box 2)] [(box 3), (box 4)]
I'm guessing that this is related to the reference of items in a iterable object, but I can't figure out the (precise) reason why the child nodes' self.childs contains not only their childs, but also sibling nodes and even parent nodes. Could you please help me to understand why this result appeared?
(full code : https://github.com/Avantgarde95/share/blob/master/btest.py )
[Solved]
Thanks to thefourtheye and the link he provided, I could understand that the problem was occurred by 'childs = []' statement. According to the link, default argument is (re-)evaluated only when python meets the 'def' keyword; so if I set the default argument 'childs' to a mutable object (such as list), after the first call of self.append, all self.childs of nodes will point the same object.
The following code now works well:
class node(object):
def __init__(self, data = 0, childs = None):
self.data = data
if childs is None:
self.childs = []
else:
self.childs = child
def append(self, *childs):
map(self.childs.append, childs)
def __str__(self):
return '(box %s)' % str(self.data)
def __repr__(self):
return self.__str__()
Executing
root = node(0)
root.append(node(1), node(2))
root.childs[0].append(node(3), node(4))
print root, root.childs, root.childs[0].childs
gives
(box 0) [(box 1) (box 2)] [(box 3) (box 4)]