Search code examples
pythonclassoopbinary-treetree-traversal

Is it a good idea to have functions whose sole purpose it is to call another function?


I wrote code for a Tree traversal. In the Binary_tree class, I wrote three methods: __Inorder, __Postorder, and __Preorder, for tree traversal; and I wrote three other methods to call them and pass in self.root as a parameter so that I don't have to pass it manually every time I want to traverse.

Code:

class Node():

    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None

class Binary_Tree():

    def __init__(self):
        self.root = None
    
    def insert(self, data):
        new_node = Node(data)
        if self.root == None:
            self.root = new_node
        else:
            current_node = self.root
            while True:
                if data > current_node.data:
                    if not current_node.right:
                        current_node.right = new_node
                        return
                    else:
                        current_node = current_node.right
                else:
                    if not current_node.left:
                        current_node.left = new_node
                        return
                    else:
                        current_node = current_node.left
    
    def inorder_traversal(self):
        return self.__Inorder(self.root)
    
    def postorder_traversal(self):
        return self.__Postorder(self.root)
    
    def preorder_traversal(self):
        return self.__Preorder(self.root)
    
    def __Inorder(self, current_node, visited_node = None):
        if visited_node is None:
            visited_node = []
    
        if current_node:
            self.__Inorder(current_node.left, visited_node)
            visited_node.append(current_node.data)
            self.__Inorder(current_node.right, visited_node)
    
        return visited_node 
        
    def __Postorder(self, current_node, visited_node = None):
        if visited_node is None:
            visited_node = []
    
        if current_node:
            self.__Postorder(current_node.left, visited_node)
            self.__Postorder(current_node.right, visited_node)
            visited_node.append(current_node.data)
    
        return visited_node
    
    def __Preorder(self, current_node, visited_node = None):
        if visited_node is None:
            visited_node = []
    
        if current_node:
            visited_node.append(current_node.data)
            self.__Preorder(current_node.left, visited_node)
            self.__Preorder(current_node.right, visited_node)
        
        return visited_node

I showed this code to a guy I know and he said "there is no point in writing a function whose sole instruction is to call another function" and it is bad code writing. So, is it true? If yes, then how should I do it?


Solution

  • Your friend is wrong.

    Your function (like inorder_traversal) is not just calling another function: it also disallows the caller to pass any arguments, which are none of their business (current_node, visited_node). And this makes it a good decision to have a clean public method, while the underscored "private" functions deal with implementation details that are reflected in their function parameters.

    Not your question, but I do see some room for avoiding code repetition, as your base functions have very similar code. Also you could consider creating generators for them instead of populating lists. You could also consider moving those private methods to the Node class.

    For example:

    class Node():
    
        def __init__(self, data):
            self.data = data
            self.left = None
            self.right = None
    
        def traverse(self, order=0):
            if order == -1:
                yield self.data
            if self.left:
                yield from self.left.traverse(order)
            if order == 0:
                yield self.data
            if self.right:
                yield from self.right.traverse(order)
            if order == 1:
                yield self.data
            
    
    class Binary_Tree():
    
        def __init__(self):
            self.root = None
        
        def insert(self, data):
            new_node = Node(data)
            if self.root == None:
                self.root = new_node
            else:
                current_node = self.root
                while True:
                    if data > current_node.data:
                        if not current_node.right:
                            current_node.right = new_node
                            return
                        else:
                            current_node = current_node.right
                    else:
                        if not current_node.left:
                            current_node.left = new_node
                            return
                        else:
                            current_node = current_node.left
        
        def preorder_traversal(self):
            if self.root:
                return self.root.traverse(-1)
    
        def inorder_traversal(self):
            if self.root:
                return self.root.traverse(0)
        
        def postorder_traversal(self):
            if self.root:
                return self.root.traverse(1)
        
    # Demo run
    tree = Binary_Tree()
    for data in (4,2,3,1,6,5,7):
        tree.insert(data)
    print(*tree.inorder_traversal())