Re: newbie: generate a function based on an expression

2005-12-14 Thread Jacob Rael
Thanks for all the suggestions and comments!!

I will try all those suggestions just to I can figure out how they
work. For phase 1 of this project, I will probably go with the eval.

thanks again, happy hacking...

jr

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: newbie: generate a function based on an expression

2005-12-13 Thread Jacob Rael
Overall I am trying to learn OOP by porting
CppSim (http://www-mtl.mit.edu/~perrott) to Python.

In CppSim, classes are defined that allow various functions to be
defined, like amplifiers. In some cases they are linear:

y = A*x

some have offsets:

y = A*x + off

some are non-linear

y = A*x - C*x**3

The coefficients and the function will remain constant once the
function is defined.

I read about the security concerns involved in using eval(). I don't
expect this project to grow to the point where I require a web
interface. However, since I am learning, I might as well learn the
right way.

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: newbie: generate a function based on an expression

2005-12-13 Thread Jacob Rael
Another example is a filter. From the CppSim doc:

Filter filt(1+1/(2*pi*fz)s,C3*s +
C3/(2*pi*fp)*s^2,C3,fz,fp,Ts,1/gain,fz,fp,Ts);

jr

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: newbie: generate a function based on an expression

2005-12-13 Thread Paul Rubin
Jacob Rael [EMAIL PROTECTED] writes:
 I read about the security concerns involved in using eval(). I don't
 expect this project to grow to the point where I require a web
 interface. However, since I am learning, I might as well learn the
 right way.

I think you're going to have to write an actual parser and evaluator.
There are various tools that can help you do that (PyParse, etc.)  but
if it's for a learning project, you might like to try doing it from
scratch.
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: newbie: generate a function based on an expression

2005-12-13 Thread Mike Meyer
Jacob Rael [EMAIL PROTECTED] writes:
 In CppSim, classes are defined that allow various functions to be
 defined, like amplifiers. In some cases they are linear:

 y = A*x

 some have offsets:

 y = A*x + off

 some are non-linear

 y = A*x - C*x**3

 The coefficients and the function will remain constant once the
 function is defined.

It appears that CppSim is a language, including parsers, evaluators,
and the other things that a language processor usually has. To do a
proper port of it, you'll have to recreate all that mechanism.

On the other hand, if you want CppSim-like program with pythonesque
expressions written in Python, then eval is probably the best way to
go - if you can live with the security concerns. An example of this is
my P(x) package, as it relates to the original F(x) package. P(x)
programs have all the power of Python, and so deserve to be treated
with the same suspicion you'd treat a Python program.

 I read about the security concerns involved in using eval(). I don't
 expect this project to grow to the point where I require a web
 interface. However, since I am learning, I might as well learn the
 right way.

Web interfaces are just one source of suspect data. That's the common
one, because that allows arbitrary users to submit data. But it's not
uncommon to not trust data from other sources as well.

In this case, you really want to let users write general-purpose
functions. The non-eval way to do that is to use closures and higher
order functions, but that's not going to look much like cppSim.

  mike
-- 
Mike Meyer [EMAIL PROTECTED]  http://www.mired.org/home/mwm/
Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information.
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: newbie: generate a function based on an expression

2005-12-13 Thread Bengt Richter
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_FAST0 (x)
  18 BINARY_MULTIPLY
  19 LOAD_GLOBAL  6 (off)
  22 BINARY_ADD
  23 CALL_FUNCTION2
  26 CALL_FUNCTION2
  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.49994
  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
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: newbie: generate a function based on an expression

2005-12-13 Thread Michael Spencer
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_FAST0 (x)
   18 BINARY_MULTIPLY
   19 LOAD_GLOBAL  6 (off)
   22 BINARY_ADD
   23 CALL_FUNCTION2
   26 CALL_FUNCTION2
   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.49994
   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


newbie: generate a function based on an expression

2005-12-12 Thread Jacob Rael
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?

jr

-- 
http://mail.python.org/mailman/listinfo/python-list


newbie: generate a function based on an expression

2005-12-12 Thread Ezequiel, Justin
 def genFun(expr, locs):
... return lambda x: eval('min(Max,max(Min,%s))' % expr, locs, {'x':
x})
... 
 fun = genFun(A*x+off, {'A': 3.0, 'off': -0.5, 'Max': 2.0,  'Min':
-2.0} )
 fun
function lambda at 0x011B1470
 fun(0)
-0.5
 fun(-10)
-2.0
 fun(10)
2.0
 
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: newbie: generate a function based on an expression

2005-12-12 Thread Michael Spencer
Jacob Rael 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?
 
 jr
 


You might prefer to write your expression as a real function, rather than a 
source string, so that it would be easier to test.

   def fun(x, A, off, Max, Min):
  ... return min(Max,max(Min,A*x + off))
  ...

Then you can curry in the constant values, using something like:

   def getFun(fn, env_dict):
  ... env_dict = env_dict.copy()
  ... def fun_curry(x):
  ... return fn(x,**env_dict)
  ... return fun_curry
  ...
   myenv = {'A': 3.0, 'off': -0.5, 'Max': 2.0,  'Min': -2.0}
   fun1 = getFun(fun, myenv)
   fun1(0)
  -0.5
   fun1(0.5)
  1.0
   fun1(-10)
  -2.0
   fun1(10)
  2.0

HTH
Michael


-- 
http://mail.python.org/mailman/listinfo/python-list


Re: newbie: generate a function based on an expression

2005-12-12 Thread Mike Meyer
Jacob Rael [EMAIL PROTECTED] writes:
 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?

 def genFun(expr, ns):
...  def fun(x):
...   newns = dict(ns)
...   newns['x'] = x
...   return eval(code, newns)
...  code = compile(min(Max, max(Min, %s)) % expr, 'genFun', 'single')
...  return fun

Having said that, this is a *very* insecure thing to do. If you'd let
us know why you want to do this, we might be able to suggest a safer
way.

mike
-- 
Mike Meyer [EMAIL PROTECTED]  http://www.mired.org/home/mwm/
Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information.
-- 
http://mail.python.org/mailman/listinfo/python-list