newbie: generate a function based on an expression

Bengt Richter bokr at oz.net
Wed Dec 14 03:21:27 CET 2005


On 12 Dec 2005 21:38:23 -0800, "Jacob Rael" <jacob.rael at gmail.com> wrote:

>Hello,
>
>I would like write a function that I can pass an expression and a
>dictionary with values. The function would return a function that
>evaluates the expression on an input. For example:
>
>fun = genFun("A*x+off", {'A': 3.0, 'off': -0.5, 'Max': 2.0,  'Min':
>-2.0} )
>
>>>> fun(0)
>-0.5
>>>> fun(-10)
>-2
>>>> fun(10)
>2
>
>so fun would act as if I did:
>
>def fun(x):
>    A = 3
>    off = -0.5
>    Max = 2
>    Min = -2
>    y = min(Max,max(Min,A*x + off))
>    return(y)
>
>Any ideas?

ISTM genFun above can't generate fun without special interpretation
of Min and Max. I.e., how is it supposed to know to use min and max (lower case)
as in your expression for y, unless you tell it to by writing

    fun = genFun("min(Max,max(Min,A*x+off))", {'A': 3.0, 'off': -0.5, 'Max': 2.0,  'Min':

or state a rule about using min and/or max when Max and/or Min are present?

Setting aside security for the moment, and guessing at the rule you might
want for Min and Max, perhaps something like (untested beyond what you see):
(also limited to using x as the single function parameter name in the expression!)

 >>> import math
 >>> allow = dict((k,v) for k,v in vars(math).items()
 ...                 if callable(v) and not k.startswith('_') or k in ('e','pi'))
 >>>
 >>> allow.update(min=min, max=max)
 >>> def vetexpr(expr, dct): return expr # XXX generate and check AST later
 ...
 >>> def genFun(expr, dct=None):
 ...     d = allow.copy()
 ...     if dct: d.update(dct)
 ...     expr = '('+expr+')'
 ...     vetexpr(expr, d) # check for illegal stuff XXX later
 ...     if 'Min' in d: expr = 'max(Min, %s)'%(expr,)
 ...     if 'Max' in d: expr = 'min(Max, %s)'%(expr,)
 ...     return eval('lambda x: %s'%expr, d)
 ...
 >>> fun = genFun("A*x+off", {'A': 3.0, 'off': -0.5, 'Max': 2.0,  'Min': -2.0})
 >>> fun
 <function <lambda> at 0x02EEBDF4>
 >>> fun(0)
 -0.5
 >>> fun(-10)
 -2.0
 >>> fun(10)
 2.0
 >>> import dis
 >>> dis.dis(fun)
   1           0 LOAD_GLOBAL              0 (min)
               3 LOAD_GLOBAL              1 (Max)
               6 LOAD_GLOBAL              2 (max)
               9 LOAD_GLOBAL              3 (Min)
              12 LOAD_GLOBAL              4 (A)
              15 LOAD_FAST                0 (x)
              18 BINARY_MULTIPLY
              19 LOAD_GLOBAL              6 (off)
              22 BINARY_ADD
              23 CALL_FUNCTION            2
              26 CALL_FUNCTION            2
              29 RETURN_VALUE

I just made things from the math module accessible in anticipation. E.g.,
 >>> sin2xd = genFun('sin(2.0*x*pi/180.)')
 >>> sin2xd(15)
 0.49999999999999994
 >>> sin2xd(0)
 0.0
 >>> sin2xd(45)
 1.0

Of course, if the dict is all constant named coefficients, you could substitute them as
literal values in the expression before generating the function. You could do that
in the placeholder vetexpr, but we'll leave that implementation for another post ;-)


Regards,
Bengt Richter



More information about the Python-list mailing list