Home > Back-end >  How to save a function as string in Python?
How to save a function as string in Python?

Time:11-12

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 the lambda) will allow you to use inspect to get the signature (.signature()) and you can combine it with re and/or ast for a much robust parser, but a 1-liner seemed more exciting
  • manages to use both eval() and exec() for an extra helping of evil
  • Related