1

I have two classes that inherit from the same base class, and they have some common methods (in fact, properties). I will need to do this:

input: an int and an object from either class;

output: the result of one of the methods (determined by the int) of the corresponding class.

I think I can use a dictionary to do this, as the following code:

        class ChangeInt(object):
            def bar(self, i):
                print(i)
        class PlusOne(ChangeInt):
            def bar(self, i):
                print(i+1)
        class PlusTwo(ChangeInt):
            def bar(self, i):
                print(i+2)
        methods_dict = {0:ChangeInt.bar}
        print(methods_dict[0](PlusOne(), 0))
        print(methods_dict[0](PlusTwo(), 0))

I expect the output to be 1,2, but I actually get this:

0
None
0
None

I would like to know how these results are generated and what should I do to fix it up. Thanks in advance.


I totally mess up the format in the comment, so I'll paste it here. thanks to Ryan Haining in Dynamic Method Call In Python 2.7 using strings of method names, I've found another way to do this:

        class ChangeInt(object):
            def bar(self, i):
                print(i)
        class PlusOne(ChangeInt):
            def bar(self, i):
                print(i+1)
        class PlusTwo(ChangeInt):
            def bar(self, i):
                print(i+2)
        methods_dict = {0:'bar'}
        getattr(PlusOne(), methods_dict[0])(0)
        getattr(PlusTwo(), methods_dict[0])(0)
Zhu
  • 71
  • 7

1 Answers1

2

This may not be the best way to do it, but it produces the expected result:

class ChangeInt(object):
    def bar(self, i):
        if not ('ChangeInt' in str(self.__class__)):
            self.bar(i)
        else:
            print(i)
class PlusOne(ChangeInt):
    def bar(self, i):
        print(i+1)
class PlusTwo(ChangeInt):
    def bar(self, i):
        print(i+2)

methods_dict = {0:ChangeInt.bar}

methods_dict[0](ChangeInt(), 0)
methods_dict[0](PlusOne(), 0)
methods_dict[0](PlusTwo(), 0)

and prints:

0
1
2

The bar() function in the base class calls the method associated with given self instance or the base class implementation if it's an instance of base class itself (just print(i)). This is important, without it the code will be calling self.bar(i) infinitely if you invoke it on the base class (i.e. until it reaches max allowable recursion call number).

The check if not ('ChangeInt' in str(self.__class__)): is necessary since issubclass will return True for the parent class too,

issubclass(class, classinfo)

Return true if class is a subclass (direct, indirect or virtual) of classinfo. A class is considered a subclass of itself. classinfo may be a tuple of class objects, in which case every entry in classinfo will be checked. In any other case, a TypeError exception is raised.

And the point is to distinguish between the base/parent class and any subclasses.

Finally, since your function calls don't return anything explicitly, they actually return None. That None is what you are printing when enclosing the calls in an additional print,

print(methods_dict[0](PlusOne(), 0))

I removed the extra print so you only print the (assuming) intended content.

atru
  • 4,699
  • 2
  • 18
  • 19
  • Thanks very much. So the problem is, since I explicitly write `ChangeInt.bar` in `methods_dict`, it will always call bar method of the `ChangeInt` class, even if I pass it an object that belongs to a subclass of `ChangeInt`. Is that right? If so, is there a way to say "the bar method of any class that inherits from ChangeInt" in `methods_dict`? – Zhu Aug 19 '19 at 00:05
  • Hey bro, I've another way to do this, thanks to Ryan Haining in https://stackoverflow.com/questions/17734618/dynamic-method-call-in-python-2-7-using-strings-of-method-names.``` class ChangeInt(object): def bar(self, i): print(i) class PlusOne(ChangeInt): def bar(self, i): print(i+1) class PlusTwo(ChangeInt): def bar(self, i): print(i+2) methods_dict = {0:'bar'} getattr(PlusOne(), methods_dict[0])(0) getattr(PlusTwo(), methods_dict[0])(0)``` – Zhu Aug 19 '19 at 00:45
  • @Zhu OK, I understand now, I was wondering why did you wanted this kind of design, it's not the healthiest :) I'll keep my answer since it works but the other approach seems much better. To me it seemed you were just always invoking `ChangeInt` method, that's how it looked from the code. But I'm not that versed in python classes to know for sure, this was a pretty non-standard case. – atru Aug 19 '19 at 02:16