Given the following math function in form of a Python function:
import math
def f(x):
a = x - math.log(x)
b = x math.log(x)
return a / x b / math.log(x)
Is there any way that I can convert this function into a string like
expr = '(x - math.log(x)) / x (x math.log(x)) / math.log(x)'
so that when I want to call the function, I can simply use it by
func = lambda x: eval(expr)
print(func(3))
# 4.364513583657809
Note that I want to keep a
and b
in the original function. In reality, I have a lot more intermediate variables. Also, I am aware sympy
could do similar tasks, but I would like to know if it is possible to convert the function to string, as it would be much more efficient to store.
Any suggestions?
CodePudding user response:
You're probably looking for a symbolic equation solver!
Sympy's lambdify feature can do this for you!
>>> fn = sympy.lambdify("x", '(x - log(x)) / x (x log(x)) / log(x)')
>>> fn(x=3)
4.364513583657809
Caution: this also uses eval
internally as @Joshua Voskamp warns about in a comment
CodePudding user response:
Your function is already a string the moment you write it to a file!
If the function is valid Python, you can then just import
it
from myfile import expr
print(expr(3)) # 4.364513583657809
WARNING Do not ever do this
If you want some incredibly evil logic for some reason, you can save your function directly with inspect.getsource(f)
and then do something like this
>>> fn_body = """def f(x):
... a = x - math.log(x)
... b = x math.log(x)
... return a / x b / math.log(x)
... """
>>> eval(f'lambda {fn_body.split("(")[1].split(")")[0]}, _={exec(fn_body)}: {fn_body.split(" ", 1)[-1].split(")")[0]})')(3)
4.364513583657809
This works by finding the parts needed to call the function, evaluating the source as one of the args (to smuggle it into your namespace), and then building an anonymous function to call it
Further Caveats
- not remotely maintainable
- extremely fragile
- will clobber or conflict with an existing function with the same name depending on use
- you will still need to
import math
or whatever other libraries - won't work with default args without more pain
- calling
eval()
first (before creating thelambda
) will allow you to useinspect
to get the signature (.signature()
) and you can combine it withre
and/orast
for a much robust parser, but a 1-liner seemed more exciting - manages to use both
eval()
andexec()
for an extra helping of evil