4

I want to pass a formula within a function parameter in Python where the formula is a combination of the other function parameters. In principle this would look like this:

myfunction(x=2,y=2,z=1,formula="x+2*y/z")
6

or more generaly:

def myformula(x,y,z,formula):
   return formula(x,y,z)

This would allow the user to choose any arithmetic expression in terms of x, y, and z without having to create a new function.

One of the possibility I foresee is to convert the string in line of code within the function. Anything possible like that in Python? Or any other ideas? Thanks

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
Simon
  • 1,942
  • 5
  • 18
  • 22

3 Answers3

9

Using sympy, you could evaluate mathematical expressions:

import sympy as sy

def myformula(formula, **kwargs):
    expr = sy.sympify(formula)
    return expr.evalf(subs=kwargs)

print(myformula(x=2,y=2,z=1,formula="x+2*y/z"))
# 6.00000000000000

print(myformula(x=2,y=2,z=1,formula="sin(x+y-z)"))
# 0.141120008059867

But note that sympy.sympify does use eval which makes it unsafe to apply to arbitrary user input since strings can be composed to trick eval into executing arbitrary Python code.

A safer alternative is to build a parser to parse a strictly limited mathematical expressions. Here are a few examples

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • suggest `return expr.subs(kwargs)` – Azat Mar 18 '16 at 13:57
  • can evaluate `print(myformula(x=2,y=2,z=1,formula="x+2*y/z>0"))` – Azat Mar 18 '16 at 13:59
  • @Azat: Unfortunately, that changes the behavior of the second example. – unutbu Mar 18 '16 at 16:59
  • @unutbu Also looking for an answer to this, and decided to look at documentation for more information. I noticed that the docs say that sympify makes use of "eval", and is therefore should be subject to the same scrutiny... Or I may be missing something, as I'm a beginning programmer: http://docs.sympy.org/latest/modules/core.html – rocksNwaves Mar 28 '18 at 18:12
  • Also, in case you are still active, what if "my formula" contains some additional arguments that don't pertain directly to the arithmetic expression you want to pass? Is that when I use one asterisk instead of two? – rocksNwaves Mar 28 '18 at 18:14
  • @rocksNwaves: Thank you for pointing this out. I was totally and utterly mistaken. (While [sympy uses `ast`](https://github.com/sympy/sympy/blob/master/sympy/parsing/sympy_parser.py) to parse expressions under the hood, I didn't realize it still falls back on calling `eval`). Here is the relevant [github issue](https://github.com/sympy/sympy/pull/12524) which may help those interested in following (or contributing to) sympy's progress on this issue. – unutbu Mar 28 '18 at 19:48
  • I've edited the post to suggest some safe parsing alternatives, but unfortunately they are not as comprehensive in their ability to parse strings into evaluable mathematical expressions. – unutbu Mar 29 '18 at 15:10
2

Your "myFormula" isn't much different than a regular lambda expression, except with the added baggage of having to parse a string into executable Python.

(lambda x,y,z: x + 2*y/z)(5, 2, 10)

As such, you could simply define myFormula as

def myFormula(*args, formula):
    formula(*args)

and call it as

myFormula(5, 2, 10, lambda x, y, z: x + 2*y/z)
chepner
  • 497,756
  • 71
  • 530
  • 681
0

You could try using a lambda function. Something like this might suit you:

def myFormula(x,y,z):
    return lambda x,y,z: x+2*y/z

This way you don't have to define a new function and you don't have to pass anything extra as an argument.

Extra info about lambda functions: http://www.diveintopython.net/power_of_introspection/lambda_functions.html

http://pythonconquerstheuniverse.wordpress.com/2011/08/29/lambda_tutorial/

https://docs.python.org/2/reference/expressions.html#lambda

xv47
  • 992
  • 3
  • 13
  • 18
  • The arguments `x`, `y`, and `z` passed to `myFormula` are ignored; the returned function is simply a function taking 3 parameters. – chepner May 15 '14 at 19:56