Search code examples
pythonobjectoopsubclass

Why is my object render function displaying None?


So I'm currently building a Html rendering program in Python which uses two base classes:

  1. SingleTag - Used for tags that don't have any children such as a

    tag

  2. ContainingTag - For tags that have nested tags such as the Html tag, Div tag etc...

Here is the file which contains those classes, along with the subclasses that inherit from them:

class ContainingTag:
    def __init__(self, children):
        self.open_tag = "<" + self.__class__.__name__.lower() + ">"
        self.close_tag = "</"+self.__class__.__name__.lower() + ">"
        self.children = children

    def render(self):
        print("\t" + self.open_tag)
        for child in self.children:
            print("\t \t" + str(child.render()))
        print("\t" + self.close_tag)
class SingleTag:
    """A class to represent an html tag"""

    # Class initialiser
    def __init__(self, inner_html):
        self.open_tag = "<" + self.__class__.__name__.lower() + ">"
        self.close_tag = "</"+self.__class__.__name__.lower() + ">"
        self.inner_html = inner_html

    # Prints html
    def render(self):
        return self.open_tag + self.inner_html + self.close_tag
class Html(ContainingTag):
    def __init__(self, children):
        super().__init__(children) 
        self.open_tag = "<!DOCTYPE html>\n"+ "<" + self.__class__.__name__.lower() + ">"
    
    def render(self):
        print(self.open_tag)

        for child in self.children:
            print("\t \t" + str(child.render()))
        print(self.close_tag)


class Head(ContainingTag):
    def __init__(self, children):
        super().__init__(children)

class Style(ContainingTag):
    def __init__(self, children):
        super().__init__(children)

class Body(ContainingTag):
    def __init__(self, children):
        super().__init__(children)  

class Div(ContainingTag):
    def __init__(self, children):
        super().__init__(children)

class P(SingleTag):
    def __init__(self, inner_html=None):
        super().__init__(inner_html=None)
        self.inner_html = inner_html

When using the render method on a SingleTag object, it renders as expected, but when using the render method on a ContainingTag, it prints 'None' after every closing tag like this:

<Opening ContainingTag>
<Closing ContainingTag>
None

Can someone explain why this keeps printing and how to fix this? Thanks.


Solution

  • A simpler approach:

    • Override the built-in __str__() method instead of creating render
    • Pass the tag name as a parameter to the class contructor, so you don't need to create a lot of subclasses (you may still want to create a HTML subclass)
    class ContainingTag:
        def __init__(self, name, children):
            self.name = name
            self.children = children
    
        def __str__(self):
            return f'<{self.name}>\n' + ''.join([str(c) for c in self.children]) + f'</{self.name}>\n'
            
    class SimpleTag:
        def __init__(self, name, html):
            self.name = name
            self.html = html
    
        def __str__(self):
            return f'<{self.name}>{self.html}</{self.name}>\n'
    
    p1=SimpleTag('P', 'Hello')
    p2=SimpleTag('P', 'World')
    d=ContainingTag('DIV', [p1,p2])
    b=ContainingTag('BODY', [d])
    
    print(str(p1))
    <P>Hello</P>
    
    print(str(b))
    <BODY>
    <DIV>
    <P>Hello</P>
    <P>World</P>
    </DIV>
    </BODY>