Skip to content
Advertisement

Calling function through string

I have a Python main program that imports another module (called actions) with multiple functions. The main program should run some things, get a string (i.e. goto(114)) and then run actions.goto(114), in which 114 is the argument to the function goto(x) in actions.

I’ve tried the obvious which was just trying to run the string but that did not work. I’ve also find the globals() method which would work if the goto(x) was inside my main module and I’ve also found the getattr method, but in this case I haven’t found any example in which I pass the function name and argument so I’m kind of lost here.

#main.py
import actions
def main():
    getc = 'goto(114)'
    result = actions.getc #this would be actions.goto(114)
    print result

#actions.py
def goto(x):
    #code
    return something

The actual program gets the string from a .txt file that another program wrote, I just made the example that way so that its simple to understand.

Advertisement

Answer

One option you could use is __getattribute__ on the action class to get the function goto, then call it with the encompassing argument. You’d need to parse it like so:

import re
import action

getc = 'goto(114)'
func, arg = re.search('(w+)((d+))', 'goto(114)').groups()

# f is the function action.goto with the argument 114 supplied as an int
# __getattribute__ allows you to look up a class method by a string name
f = action.__getattribute__(func)

# now you can just call it with the arg converted to int
result = f(int(arg))

The regex might need to be refined a bit, but it’s looking for the name of the calling function, and the arguments wrapped in parentheses. The __getattribute__ will get the function object from action and return it uncalled so you can call it later.

For multiple arguments you can leverage the ast library:

import re
import ast

# I'm going to use a sample class as a stand-in
# for action
class action:
    def goto(*args):
        print(args)

getc = 'goto(114, "abc", [1,2,3])'
func, args = re.search('(w+)((.*))', getc).groups()

# will convert this into python data structures
# safely, and will fail if the argument to literal_eval
# is not a python structure
args = ast.literal_eval('(%s)' % args)

f = getattr(action, func)
f(*args)
# 114, "abc", [1,2,3]

The easier option (proceed with caution) would be to use eval:

cmd = 'action.%s' % getc
result = eval(cmd)

Note that this is considered bad practice in the python community, though there are examples in the standard library that use it. This is not safe for un-validated code, and is easily exploited if you don’t monitor your source file

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement