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 -- http://mail.python.org/mailman/listinfo/python-list