follow me on Twitter
    hacker emblem

    Page trailPage trail: Python tree implementation

    Python tree implementation

    IconDetails...


    Python tree implementation

    Example usage:

    testTree = Tree()  # instantiate Tree object
    testTree.appendNode(None, "Harry")  # build tree
    testTree.appendNode(0, "Jane")
    testTree.appendNode(0, "Bill")
    testTree.appendNode(1, "Joe")
    testTree.appendNode(1, "Diane")
    testTree.appendNode(4, "George")
    testTree.appendNode(4, "Mary")
    testTree.appendNode(5, "Jill")
    testTree.appendNode(7, "Carol")
    testTree.appendNode(2, "Grace")
    testTree.appendNode(1, "Mark")
    
    # testTree.deleteNode(1)
    testTree.show(0)
    print testTree.isLastNode(1)
    
    for node in testTree.expandTree(mode=_DEPTH):  # generator-based iteration
        print testTree[node].getName()    
    
    path = ""
    for node in testTree.genBPath(8):
        path += "/" + node[1]
    print path
        
    treeWalk = testTree.getBPath(8)
    print treeWalk
    path = ""
    for node in treeWalk:
        path += str(node[0])
    print path
    print "".join(["%s" % (node[0]) for node in treeWalk])
    

    Source code:

    Tree.py

    # Copyright (c) 2010, Brett Alistair Kromkamp - brettkromkamp@gmail.com
    # All rights reserved.
    # 
    # Redistribution and use in source and binary forms, with or without modification,
    # are permitted provided that the following conditions are met:
    # 
    # Redistributions of source code must retain the above copyright notice, this list
    # of conditions and the following disclaimer.
    # 
    # Redistributions in binary form must reproduce the above copyright notice, this
    # list of conditions and the following disclaimer in the documentation and/or
    # other materials provided with the distribution.
    # 
    # Neither the name of the copyright holder nor the names of the contributors
    # may be used to endorse or promote products derived from this software without
    # specific prior written permission.
    # 
    # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
    # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
    # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    
    
    class TreeError(Exception):  # TODO: expand with error-code functionality
        """Basic tree exception class"""
    
        def __init__(self, value):
            self.value = value
    
        def __str__(self):
            return repr(self.value)
    
    #--------------------------------------------------------------------------------
    
    # module constants
    _ADD = 0
    _DELETE = 1
    _INSERT = 2
    
    class Node(object):
        """Class for basic node functionality"""
    
        def __init__(self, id, name):
            self.id = id
            self.name = name
            self.bpointer = None
            self.fpointer = []
            # self.expanded = True
            self.properties = BaseProperties()        
    
        def getId(self):
            return self.id
    
        def getName(self):
            return self.name
    
        def setName(self, name):
            self.name = name
    
        def setFPointer(self, id, mode, fpointer=None):
            if fpointer is None:
                if mode == _ADD:
                    self.fpointer.append(id)
                elif mode == _DELETE:
                    self.fpointer.remove(id)
                elif mode == _INSERT:
                    self.fpointer = [id]
            else:
                self.fpointer = fpointer
    
        def getFPointer(self):
            return self.fpointer
    
        def setBPointer(self, id):
            self.bpointer = id
    
        def getBPointer(self):
            return self.bpointer
    
        def listNode(self):  # NOTE: for debugging purposes
            print ["%s: %s" % (k, v) for k, v in self.__dict__.items()]
    
    #--------------------------------------------------------------------------------
    
    from Node import Node
    
    class EmbeddedNode(Node):
        """Class that adds 'multi-dimensional' functionality to the Node-class,
           i.e., 'trees within trees'"""
    
        def __init__(self, id, name, tree=None):
            Node.__init__(self, id, name)  # explicit call to ancestor's __init__ method
            self.embeddedTree = None  # place-holder for embedded tree / lower level
            self.superPath = []  # place-holder for nodes' super path        
    
        def setTree(self, tree):
            self.embeddedTree = tree
    
        def getTree(self):
            return self.embeddedTree
    
        def deleteTree(self):
            self.embeddedTree = None
            
       def addSuperPath(self, path):
            self.superPath.insert(0, path)
            
        def getSuperPath(self):
            return self.superPath
    
    #--------------------------------------------------------------------------------
    
    import string
    
    from EmbeddedNode import EmbeddedNode
    from TreeError import TreeError
    
    # module constants
    (_ADD, _DELETE, _INSERT) = range(3)
    (_ROOT, _DEPTH, _WIDTH) = range(3)
    
    class Tree(object):
        """Class for basic tree functionality"""
    
        def __init__(self):
            self.tree = []
            self.branches = [[]]
    
        def clear(self):
            self.tree = []
    
        def appendNode(self, pos, name):
            """Append a node after the position indicated by the 'pos' parameter"""
    
            # if self.isUniqueName(name):
            if pos is None:  # root node
                id = _ROOT
            else:
                id = self.__generateId()
            node = EmbeddedNode(id, name)  # create node
            self.tree.append(node)
            self.__updateFPointer(pos, id, _ADD)
            node.setBPointer(pos)
            # else:
                # raise TreeError("Node name is not unique: %s" % (name))
    
        def insertNode(self, pos, name):
            """Insert a node between the node indicated by the 'pos' 
               parameter and its parent node"""
    
            # if self.isUniqueName(name):
                # index = self.getIndex(pos)
            id = self.__generateId()
            # prevPointer = self[index].getBPointer()
            prevPointer = self[pos].getBPointer()
            newNode = EmbeddedNode(id, name)  # create node
            newNode.setBPointer(prevPointer)
            newNode.setFPointer(pos, _ADD)
            self.tree.append(newNode)
            self.__updateFPointer(prevPointer, id, _ADD)
            # self[index].setBPointer(id)
            self[pos].setBPointer(id)
            self.__updateFPointer(prevPointer, pos, _DELETE)
            # else:
                # raise TreeError("Not unique node name: %s" % (name))
    
        def moveNode(self, src, dest):  # TODO: test method
            """Move a node indicated by the 'src' parameter to the node indicated 
               by the 'dest' parameter"""
            
            # test/debug: '3' is root, and the source and destination node
            if (src != _ROOT and len(self.tree) > 3):  
                previousNode = self[src].getBPointer()
                self.__updateFPointer(previousNode, src, _DELETE)
                self.__updateFPointer(dest, src, _ADD)
                self[src].setBPointer(dest)
            else:
                raise TreeError("Invalid move operation")
    
        def copyNode(self, src, dest):
            """Copy a node indicated by the 'src' parameter to the 
               node indicated by the 'dest' parameter"""
            pass
    
        def deleteNode(self, pos):
            """Delete a node in the position indicated by the 'pos' parameter"""
    
            if pos in self:
                # index = self.getIndex(pos)
                # previousNode = self[index].getBPointer()
                previousNode = self[pos].getBPointer()
                self.__updateFPointer(previousNode, pos, _DELETE)
                # for node in self[index].getFPointer():
                for node in self[pos].getFPointer():
                    self.__updateFPointer(previousNode, node, _ADD)
                    self.__updateBPointer(node, previousNode)
                # self.tree.remove(self[index])
                self.tree.remove(self[pos])
            else:
                raise TreeError("Node does not exist: %s" % (pos))
    
        def deleteBranch(self, pos):
            deleteList = [pos]
    
            def innerDelete(pos):  # inner function to 'deleteBranch'
                # fpointer = self[self.getIndex(pos)].getFPointer()
                fpointer = self[pos].getFPointer()
                if fpointer != []:
                    for node in fpointer:
                        # deleteList.append(self[self.getIndex(node)].getId())  # NOTE: verify
                        deleteList.append(self[node].getId())  # NOTE: verify
                        innerDelete(node)
                    else:
                        return
    
            if self.isBranch(pos):
                innerDelete(pos)  # call inner-function
                for node in deleteList:
                    self.deleteNode(node)
            else:
                raise TreeError("Node contains no subnodes, i.e., not a branch: %s" % (pos))
    
        def setName(self, pos, name):
            if pos in self:
                self[pos].setName(name)
            else:
                raise TreeError("Node does not exist: %s" % (pos))
        
        
        def genBPath(self, pos):
            # Python generator. Performs the inverse of 'expandTree'.
            yield (self[pos].getId(), self[pos].getName())
            bpointer = self[pos].getBPointer()
            yield (bpointer, self[bpointer].getName())
            while bpointer:
                bpointer = self[bpointer].getBPointer()
                yield (bpointer, self[bpointer].getName())
        
        def getBPath(self, pos, pPath=[]):
            if pPath == []:
                path = []
            else:
                path = pPath        
            bpointer = self[pos].getBPointer()
            if (bpointer == 0) or (bpointer is None):
                path.insert(0, (self[pos].getId(), self[pos].getName()))
            else:
                path.insert(0, (self[pos].getId(), self[pos].getName()))
                self.getBPath(bpointer, path)  # recursive call
            return path
    
        def getIndex(self, pos=None):
            if pos is None:
                return _ROOT
            if pos in self:
                for index, node in enumerate(self.tree):
                    if node.getId() == pos:
                        break
                return index
            else:
                raise TreeError("Node does not exist: %s" % (pos))
    
        def show(self, pos, level=_ROOT):
            
            queue = self[pos].getFPointer()
            if queue:  # branch
                if level == _ROOT:
                    print ">", str(self[pos].getId()).rjust(3), "-", 
                        self[pos].getName(), self.getIndex(pos), self.isLastNode(pos)
                else:
                    print "t"*level, ">", str(self[pos].getId()).rjust(3), "-", 
                        self[pos].getName(), self.getIndex(pos), self.isLastNode(pos)
                # if self[pos].getExpansion():
                if self[pos].properties.getPublic():
                    level += 1
                    for element in queue:
                        self.show(element, level)  # recursive call
            else:  # leaf
                if level == _ROOT:
                    print ">", str(self[pos].getId()).rjust(3), "-", 
                        self[pos].getName(), self.getIndex(pos), self.isLastNode(pos)
                else:
                    print "t"*level, ">", str(self[pos].getId()).rjust(3), "-",
                        self[pos].getName(), self.getIndex(pos), self.isLastNode(pos)
                    
        def genBranchLines(self, index=0, pos=_ROOT, level=_ROOT, childX=0, childY=0):
                
            def connect(child, parent):  # inner function to genBranchLines
                rowStart = parent[0] + 1
                rowEnd = child[0]
                column =  child[1] -1
                if column != -1:
                    for y, row in enumerate(self.branches[index]):
                        if (y >= rowStart) and (y <= rowEnd):
                            row[column] = 1
        
            parentY = childY
            parentX = childX
            
            queue = self[pos].getFPointer()
            if queue:  # branch
                self.branches[index].append([0]*(level + 1))
                if level != _ROOT:
                    childX += 1            
                childY = len(self.branches[index]) -1
                connect((childY, childX), (parentY, parentX))
                # if self[pos].getExpansion():
                if self[pos].properties.getPublic():
                    level += 1
                    for element in queue:
                        # recursive call
                        self.genBranchLines(index, element, level, childX, childY)
            else:  # leaf
                self.branches[index].append([0]*(level + 1))
                if level != _ROOT:
                    childX += 1
                childY = len(self.branches[index]) -1
                connect((childY, childX), (parentY, parentX))
    
        def isLastNode(self, pos):
            result = False
            parentNode = self[pos].getBPointer()
            if parentNode != None:
                branch = self[parentNode].getFPointer()
                if branch.index(self[pos].getId()) == (len(branch) -1):
                    result = True
            return result
        
        def expandTree(self, pos=_ROOT, mode=_DEPTH):
            # Python generator. Loosly based on an algorithm from 'Essential LISP' by
            # John R. Anderson, Albert T. Corbett, and Brian J. Reiser, page 239-241
            yield pos
            queue = self[pos].getFPointer()
            while queue:
                yield queue[0]
                expand = self[queue[0]].getFPointer()
                if mode == _DEPTH:
                    queue = expand + queue[1:]  # depth-first
                elif mode == _WIDTH:
                    queue = queue[1:] + expand  # width-first
    
        def isBranch(self, pos):
            # return self[self.getIndex(pos)].getFPointer()
            return self[pos].getFPointer()
    
        def isUniqueName(self, name):
            for node in self.tree:
                if node.getName() == name:
                    return False
            return True
    
        def __updateFPointer(self, pos, id, mode):
            if pos is None:
                return
            else:
                # self[self.getIndex(pos)].setFPointer(id, mode)
                self[pos].setFPointer(id, mode)
    
        def __updateBPointer(self, pos, id):
            # self[self.getIndex(pos)].setBPointer(id)
            self[pos].setBPointer(id)
    
        def __generateId(self):
            """Private function that generates an unique node-ID"""
    
            nodes = [node.getId() for node in self.tree]
            result = max(nodes) +1
            return result
    
        def __getitem__(self, key):
            return self.tree[self.getIndex(key)]
    
        def __setitem__(self, key, item):
            self.tree[self.getIndex(key)] = item
    
        def __len__(self):
            return len(self.tree)
    
        def __nonzero__(self):
            if self.tree == []:
                return False
            else:
                return True
    
        def __contains__(self, pos):
            return [node.getId() for node in self.tree if node.getId() == pos]
    
    #--------------------------------------------------------------------------------
    # module testing
    if __name__ == "__main__":
        testTree = Tree()  # instantiate Tree object
        testTree.appendNode(None, "Harry")  # build tree
        testTree.appendNode(0, "Jane")
        testTree.appendNode(0, "Bill")
        testTree.appendNode(1, "Joe")
        testTree.appendNode(1, "Diane")
        testTree.appendNode(4, "George")
        testTree.appendNode(4, "Mary")
        testTree.appendNode(5, "Jill")
        testTree.appendNode(7, "Carol")
        testTree.appendNode(2, "Grace")
        testTree.appendNode(1, "Mark")
        # testTree.deleteNode(1)
        testTree.show(0)
        print testTree.isLastNode(1)
        # testTree.tree[2].addAttribute(["OS", "Linux"])
        # for node in testTree.tree:
            # print node, node.listNode()
    
        # for node in testTree.expandTree(mode=_DEPTH):  # generator-based iteration
            # print testTree[node].getName()
        
        """
        path = ""
        for node in testTree.genBPath(8):
            path += "/" + node[1]
        print path
        """
        """
        treeWalk = testTree.getBPath(8)
        print treeWalk
        path = ""
        for node in treeWalk:
            # path += "/" + node[1]
            path += str(node[0])
        print path
        print "".join(["%s" % (node[0]) for node in treeWalk])
        """
    

    Click here to be able to create pages, upload images and file attachments, and link to other users and their pages.


    blog comments powered by Disqus