Bengt Richter wrote: > On 12 Dec 2005 21:38:23 -0800, "Jacob Rael" <[EMAIL PROTECTED]> 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 Here's another take on the source-composition approach:
Setup a useful enviroment: def makenamespace(): """Get some useful, and (I hope) safe functions""" import math, __builtin__ modules = { math:['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', 'degrees', 'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log', 'log10', 'modf', 'pi', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh'], __builtin__:['max', 'min', 'pow'] } ns = dict((n, getattr(mod, n)) for mod in modules for n in modules[mod]) return ns def compose_source(expr, presets, namespace = makenamespace()): """Return a function whose arguments are inferred from expr and whose constants are found in presets""" func = compile(expr, "src", "eval") names = func.co_names arguments = ",".join(name for name in names if name not in presets and name not in namespace) sourcelines = \ ["def func(%s):" % arguments] +\ ["%s=%s" % (name, value) for name, value in presets.iteritems()] +\ ["return %s" % expr] composed = "\n ".join(sourcelines) + "\n" exec composed in namespace return namespace["func"] >>> f = compose_source("min(a * b, max(c* d))",{"a": 3, "b": 4, "c": 5}) f is now a function only of d, since a,b and c are constants: >>> import inspect >>> inspect.formatargspec(inspect.getargspec(f)) '((d,), None, None, None)' >>> f = compose_source("min(a * b, max(c* d))",{"d": 3, "b": 4, "c": 5}) >>> inspect.getargspec(f) -- http://mail.python.org/mailman/listinfo/python-list