pythonpolymorphism

Method overloading for different argument type in python


I'm writing a preprocessor in python, part of which works with an AST.

There is a render() method that takes care of converting various statements to source code.

Now, I have it like this (shortened):

def render(self, s):
    """ Render a statement by type. """

    # code block (used in structures)
    if isinstance(s, S_Block):
        # delegate to private method that does the work
        return self._render_block(s)

    # empty statement
    if isinstance(s, S_Empty):
        return self._render_empty(s)

    # a function declaration
    if isinstance(s, S_Function):
        return self._render_function(s)

    # ...

As you can see, it's tedious, prone to errors and the code is quite long (I have many more kinds of statements).

The ideal solution would be (in Java syntax):

String render(S_Block s)
{
    // render block
}

String render(S_Empty s)
{
    // render empty statement
}

String render(S_Function s)
{
    // render function statement
}

// ...

Of course, python can't do this, because it has dynamic typing. When I searched for how to mimick method overloading, all answers just said "You don't want to do that in python". I guess that is true in some cases, but here kwargs is really not useful at all.

How would I do this in python, without the hideous kilometre-long sequence if type checking ifs, as shown above? Also, preferably a "pythonic" way to do so?

Note: There can be multiple "Renderer" implementations, which render the statements in different manners. I can't therefore move the rendering code to the statements and just call s.render(). It must be done in the renderer class.

(I've found some interesting "visitor" code, but I'm not sure if it's really the thing I want).


Solution

  • Would something like this work?

    self.map = {
                S_Block : self._render_block,
                S_Empty : self._render_empty,
                S_Function: self._render_function
    }
    def render(self, s):
        return self.map[type(s)](s)
    

    Keeping a reference to a class object as a key in a dictionary and having it's value be the function object you want to call will make your code shorter and less error prone. The only place an error could occur here would be in the definition of the dictionary. Or one of your internal functions of course.