Jeremy Sanders wrote:

Colin J. Williams wrote:

Could you not have functions a and b each of which returns a NumArray

instance?

Your expression would then be something like a(..)+2*b(..).

The user enters the expression (yes - I'm aware of the possible security

issues), as it is a scientific application. I don't think they'd like to

put () after each variable name.

I could always munge the expression after the user enters it, of course.

Jeremy

Alternatively, you could build your own expression calculator, and initialize

the objects if necessary as they are evaluated. If you are happy with Python

syntax for your expressiones then the stdlib compiler package is helpful. The

example below is not tested beyond what you see. It's a bit verbose, but most

of the code is boilerplate.

a = 3

b = 4

calc('a * b')
using a

using b

12 calc('a * b ** (b - a) * "a"')
using a

using b

using b

using a

'aaaaaaaaaaaa' calc("0 and a or b")
using b

4 calc("1 and a or b")
using a

3 calc("1 and a or c")
using a

3 calc("0 and a or c")
Undefined symbol: c

HTH, Michael

-----------------

import compiler

class CalcError(Exception):

def __init__(self,error,descr = None,node = None):

self.error = error

self.descr = descr

self.node = node

def __repr__(self):

return "%s: %s" % (self.error, self.descr)

__str__ = __repr__

class LazyCalc(object):

def __init__(self, namespace):

self._cache = {} # dispatch table

self.context = namespace

def visit(self, node,**kw):

cls = node.__class__

meth = self._cache.setdefault(cls,

getattr(self,'visit'+cls.__name__,self.default))

return meth(node, **kw)

def visitExpression(self, node, **kw):

return self.visit(node.node)

# Binary Ops

def visitAdd(self,node,**kw):

return self.visit(node.left) + self.visit(node.right)

def visitDiv(self,node,**kw):

return self.visit(node.left) / self.visit(node.right)

def visitFloorDiv(self,node,**kw):

return self.visit(node.left) // self.visit(node.right)

def visitLeftShift(self,node,**kw):

return self.visit(node.left) << self.visit(node.right)

def visitMod(self,node,**kw):

return self.visit(node.left) % self.visit(node.right)

def visitMul(self,node,**kw):

return self.visit(node.left) * self.visit(node.right)

def visitPower(self,node,**kw):

return self.visit(node.left) ** self.visit(node.right)

def visitRightShift(self,node,**kw):

return self.visit(node.left) >> self.visit(node.right)

def visitSub(self,node,**kw):

return self.visit(node.left) - self.visit(node.right)

# Unary ops

def visitNot(self,node,*kw):

return not self.visit(node.expr)

def visitUnarySub(self,node,*kw):

return -self.visit(node.expr)

def visitInvert(self,node,*kw):

return ~self.visit(node.expr)

def visitUnaryAdd(self,node,*kw):

return +self.visit(node.expr)

# Flow Control

def visitAnd(self,node,**kw):

for arg in node.nodes:

val = self.visit(arg)

if not val:

return val

return val

def visitOr(self,node,**kw):

for arg in node.nodes:

val = self.visit(arg)

if val:

return val

return val

# Logical Ops

def visitBitand(self,node,**kw):

return reduce(lambda a,b: a & b,[self.visit(arg) for arg in node.nodes])

def visitBitor(self,node,**kw):

return reduce(lambda a,b: a | b,[self.visit(arg) for arg in node.nodes])

def visitBitxor(self,node,**kw):

return reduce(lambda a,b: a ^ b,[self.visit(arg) for arg in node.nodes])

def visitCompare(self,node,**kw):

comparisons = {

"<": operator.lt, # strictly less than

"<=": operator.le,# less than or equal

">": operator.gt, # strictly greater than

">=": operator.ge, # greater than or equal

"==": operator.eq, # equal

"!=": operator.ne, # not equal

"<>": operator.ne, # not equal

"is": operator.is_, # object identity

"is not": operator.is_not # negated object identity

}

obj = self.visit(node.expr)

for op, compnode in node.ops:

compobj = self.visit(compnode)

if not comparisons[op](obj, compobj):

return False

obj = compobj

return True

# Values

def visitCallFunc(self,node,**kw):

raise CalcError("Functions not supported", node.node)

def visitName(self, node, **kw):

"""LazyEvaluation"""

name = node.name

try:

val = eval(name, self.context)

except NameError:

raise CalcError("Undefined symbol",name)

except:

raise

print "using %s" % name # init if necessary here

return val

def visitConst(self, node, **kw):

return node.value

# Other

def default(self, node, **kw):

"""Anything not expressly allowed is forbidden"""

raise CalcError("Not Allowed",

node.__class__.__name__,node)

def calc(source, context = None):

walker = LazyCalc(context or globals())

try:

ast = compiler.parse(source,"eval")

except SyntaxError, err:

raise

try:

return walker.visit(ast)

except CalcError, err:

return err