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