Search code examples
pythonpython-3.xparentparent-childtree-structure

Python TreeStructure (parent and children) class, children gets appended to it's own children?


I'm coding a TreeStructure (TS) class, which lets me create parent and child objects, which are linked to each other. Every TS -object has m_parent attribute, which is controlled with parent property, and also they have children list, which holds in all the children of this parent. Whenever I add a child to it's parent's children list, it gets added to it's own children list too? Here's what I have:

ROOT = "__ROOT__"

class TreeStructure:

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

    @property
    def parent(self):
        '''Returns m_parent'''
        if hasattr(self, "m_parent"):
            return self.m_parent
        else:
            return None

    @parent.setter
    def parent(self, parent=ROOT):
        '''Sets m_parent'''
        if type(parent) == type(self):
            if self.parent:
                del self.parent
            self.m_parent = parent
            self.m_parent.children.append(self)
        elif parent == ROOT:
            if self.parent:
                del self.parent
            self.m_parent = ROOT
        else:
            raise TypeError("Parent's type %s did not match objects type %s"
                            %(type(parent), type(self)))

    @parent.deleter
    def parent(self):
        '''Deletes m_parent'''
        if self.parent:
            if self.parent != ROOT:
                self.m_parent.children.remove(self)
            del self.m_parent

And now by creating two simple objects, it should work. However, it doesn't.

a = TreeStructure()
b = TreeStructure(a)

The problem appears at line 25, self.m_parent.children.append(self). If I add print's to both sides of that line, I see that both print(self.m_parent.children) and print(self.children) print an empty list [] BEFORE the append line. Now if I add the prints AFTER the append line, both prints will say [<__main__.TreeStructure object at 0x...>], which should only happen for the parent, not the child?


Solution

  • Do not use [] as a default value!

    >>> def bad(default=[]):
    ...     default.append(1)
    ...     print default
    ... 
    >>> bad()
    [1]
    >>> bad()
    [1, 1]
    >>> bad()
    [1, 1, 1]
    >>> def good(default=None):
    ...     if default is None:
    ...             default = []
    ...     default.append(1)
    ...     print default
    ... 
    >>> good()
    [1]
    >>> good()
    [1]
    

    Default arguments are created when the function is defined, not when it is called. So only use non-mutable types for defaults. using integers, strings, tuples is okay, but if you want a default list or dictionary or anything mutable then use None and do the above trick.

    Read this question and answers for better understanding of this matter.