If you change the syntax used in the formulas slightly, (another) way to do this — as I mentioned in a comment — would be to use string.Template
substitution.
Out of curiosity I decided to find out if this other approach was viable — and consequently was able to come up with better answer in the sense that not only is it simpler than my other one, it's also a little more flexible in the sense that it would be easy to add arguments to the functions being called as noted in a comment below.
from string import Template
def FooFunction(): return 15
def BarFunction(): return 30
def BazFunction(): return 6
formula = "(($foo + $bar) - ($baz/2))"
function_mapping = dict(foo='FooFunction()', # note these calls could have args
bar='BarFunction()',
baz='BazFunction()')
converted_formula = Template(formula).substitute(function_mapping)
print('converted_formula = "{}"'.format(converted_formula))
# define contexts in which to evalute the expression
global_context = dict(FooFunction=FooFunction,
BarFunction=BarFunction,
BazFunction=BazFunction)
local_context = dict(__builtins__=None)
function = lambda: eval(converted_formula, global_context, local_context)
answer = function() # call it
print('answer = {}'.format(answer))
As a final note, notice that string.Template
supports different kinds of Advanced usage which would allow you to fine-tune the expression syntax even further — because internally it uses the re
module (in a more sophisticated way than I did in my original answer).
For the cases where the mapped functions all return values that can be represented as Python literals — like numbers — and aren't being called just for the side-effects they produce, you could make the following modification which effectively cache (aka memoize) the results:
function_cache = dict(foo=FooFunction(), # calls and caches function results
bar=BarFunction(),
baz=BazFunction())
def evaluate(formula):
print('formula = {!r}'.format(formula))
converted_formula = Template(formula).substitute(function_cache)
print('converted_formula = "{}"'.format(converted_formula))
return eval(converted_formula, global_context, local_context)
print('evaluate(formula) = {}'.format(evaluate(formula)))
Output:
formula = '(($foo + $bar) - ($baz/2))'
converted_formula = "((15 + 30) - (6/2))"
evaluate(formula) = 42