'How to write string based dynamic function dispatch in python from within a class?

I want to dynamically determine function dispatch in my python code. Like so : https://docs.python.org/3/faq/programming.html#how-do-i-use-strings-to-call-functions-methods

I build a dispatch dictionary wherein: dispatch = {'foo' : foo_function, 'bar': bar:function}

And then from within code, I can call the appropriate function based on my data string by saying: dispatch[data]()

So far so good. My issue is I want this functionality inside a class accessible to it's methods:

class Demo(object):
    """ Demonstrate the issue
    """

    dispatch = {'foo' : self.call_foo, 'bar': self.call_bar}

    def parse_component(self, data, some_param):
        self.__class__.dispatch[data](self, some_param)

    def call_foo(self, some_param):
        """ Do some intelligent work here"""
        pass

    def call_bar(self, some_param):
        """ Do some intelligent work here"""
        pass

The problem is that dispatch table is a class variable and does not have a notion of the instance methods self.call_foo and self.call_bar. I tried using get_attr :

class Demo(object):
     dispatch = {'foo': getattr(Demo, 'call_foo')}

I get a NameError on Demo with this. It does not recognize the class name from within the class which I guess makes sense.

Other than making dispatch another instance method, is there any other way to do this? Ideally dispatch should be a class variable/method since it is constant to the class.



Solution 1:[1]

The functions Demo.call_foo and Demo.call_bar are functions which take in an instance self and some_param. When you create an instance of Demo, e.g. d = Demo(), those functions now become bound methods where the self parameter is already passed for you. But the other form still exists, so you can call instance methods with ClassName.method(instance, ...). For example:

class Demo:
    def call_foo(self):
        print("called foo")

Calling it both ways works:

>>> d = Demo()
>>> Demo.call_foo(d)
called foo
>>> d.call_foo()
called foo

This is relevant to your question because inside the class definition, you don't have an instance self to use, but you can refer to the functions directly. Since they are functions defined in the class scope, they can be referenced directly like any other class attribute.

But like we've seen above, you can pass the instance along to the function. You're already doing that in parse_component. So the only problem with your current code is that you tried to use self to reference the methods. But since the dict is defined in the class scope, you can just refer to them by name:

class Demo:
    
    def parse_component(self, data, some_param):
        self.__class__.dispatch[data](self, some_param)

    def call_foo(self, some_param):
        print("called foo with", some_param)

    def call_bar(self, some_param):
        print("called bar with", some_param)

    dispatch = {'foo' : call_foo, 'bar': call_bar}

And using it:

>>> d = Demo()
>>> d.parse_component("foo", 2)
called foo with 2
>>> d.parse_component("bar", 3)
called bar with 3
>>> Demo.parse_component(d, "foo", 4)
called foo with 4

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 alkasm