15

I have a tuple which lists down the methods of a class like:

t = ('methA','methB','methC','methD','methE','methF')

and so on..

Now I need to dynamically call these methods based on a user made selection. The methods are to be called based on the index. So if a user selects '0', methA is called, if '5', methF is called.

My method for doing this is as follows:

def makeSelection(self, selected):
    #methodname = t[selected]
    #but as this is from  within the class , it has to be appended with 'self.'methodname
    # also need to pass some arguments locally calculated here

I have managed to work out something with eval but it yields error and is not at all elegant.

Ryan Haining
  • 35,360
  • 15
  • 114
  • 174
bhaskarc
  • 9,269
  • 10
  • 65
  • 86

1 Answers1

36

If you're calling a method on an object (including imported modules) you can use:

getattr(obj, method_name)(*args) #  for this question: use t[i], not method_name

for example:

>>> s = 'hello'
>>> getattr(s, 'replace')('l', 'y')
'heyyo'

If you need to call a function in the current module

getattr(sys.modules[__name__], method_name)(*args)

where args is a list or tuple of arguments to send, or you can just list them out in the call as you would any other function. Since you are in a method trying to call another method on that same object, use the first one with self in place of obj

getattr takes an object and a string, and does an attribute lookup in the object, returning the attribute if it exists. obj.x and getattr(obj, 'x') achieve the same result. There are also the setattr, hasattr, and delattr functions if you want to look further into this kind of reflection.



A completely alternative approach:
After noticing the amount of attention this answer has received, I am going to suggest a different approach to what you're doing. I'll assume some methods exist

def methA(*args): print 'hello from methA'
def methB(*args): print 'bonjour de methB'
def methC(*args): print 'hola de methC'

To make each method correspond to a number (selection) I build a dictionary mapping numbers to the methods themselves

id_to_method = {
    0: methA,
    1: methB,
    2: methC,
}

Given this, id_to_method[0]() would invoke methA. It's two parts, first is id_to_method[0] which gets the function object from the dictionary, then the () calls it. I could also pass argument id_to_method[0]("whatever", "args", "I", "want) In your real code, given the above you would probably have something like

choice = int(raw_input('Please make a selection'))
id_to_method[choice](arg1, arg2, arg3) # or maybe no arguments, whatever you want
Ryan Haining
  • 35,360
  • 15
  • 114
  • 174
  • What would a nice solution be if we were considering instance methods? I.e.: imagine the methods you wrote now include the self argument: def methA(self, *args): print 'hello from methA' but I would like to build the dictionary in the class (so that the keys can be read without necessarily having an instance) and not in the constructor. How could one do that? – marcotama Mar 11 '15 at 01:07
  • You could use unbound methods and `getattr` similarly as here. I'm not sure I understand what you want. – Ryan Haining Mar 11 '15 at 14:02
  • Objects of my class X have alternative "exploration" methods. Function y interfaces with the user and calls the desired exploration method. Rather than having if-elif-elif-... I would prefer to access a dictionary because it looks better. Also, if the dictionary was a class variable, I could access its keys from outside. The issue here is that these exploration methods depend on instance variables. I also thought (and implemented) the `getattr` trick, but it just does not look pretty at all. – marcotama Mar 11 '15 at 23:09
  • @tamzord sorry I haven't been on in a couple of weeks. What's wrong with using an unbound method? – Ryan Haining Mar 27 '15 at 18:14
  • getattr(self, dynamic_method_name)(args) for dynamically accessing method inside a method of a class – Jebin Oct 07 '16 at 23:15