435,247 Members | 1,057 Online
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,247 IT Pros & Developers. It's quick & easy.

# How do I convert arithemtic string (like "2+2") to a number?

 P: n/a Hello, I need to convert a string to a number, but the string can contain +,-,* and / as well as parenthesis. For example, if I have the string "30/(6+9)" I would like a function that returned the number 2. I actually wrote a java function that did this a couple of years ago, in school, as an excersise in "binary trees". I lost it, and most of my programming knowledge, but I figured perhaps there is a way to do this easily in python? It seems to me like it could be a common problem. /Arvid Andersson Jul 18 '05 #1
8 Replies

 P: n/a Use the eval function: eval("30/(6+9)") 2 Michael Jul 18 '05 #2

 P: n/a On Sat, Feb 05, 2005 at 02:11:07PM -0800, Michael Hartl wrote: Use the eval function: eval("30/(6+9)") 2 Thanks, just what I was looking for! Jul 18 '05 #3

 P: n/a On Sat, 2005-02-05 at 17:11, Michael Hartl wrote: Use the eval function: eval("30/(6+9)") 2 Michael Sure, if you trust the source of the string you are evaluating. If this is a form submission on your web site, be wary of the nefarious user who might submit to you: commands.getoutput( "rm -rf %(HOME)s"%os.environ ) as the "expression" to evaluate. Adam DePrince Jul 18 '05 #4

 P: n/a Adam brings up a good point: eval is a very general function which evaluates an arbitrary Python expression. As a result, it (and its close cousin exec) should be used with caution if security is an issue. Michael Jul 18 '05 #5

 P: n/a Michael Hartl wrote: Adam brings up a good point: eval is a very general function which evaluates an arbitrary Python expression. As a result, it (and its close cousin exec) should be used with caution if security is an issue. To get a secure eval for simple mathematical expressions, it should suffice to check the string in the following way: It does not contain characters other than operators, numbers, dots and parentheses (perhaps you want to allow 'e' for floats, but you should make sure that the 'e' is surrounded by numbers or optionally followed by a +/- sign). If you want to go a step further, you could parse the string to eval with the parser/tokenize/... modules and verify the parse tree that it contains nothing except operators and numbers. Reinhold Jul 18 '05 #6

 P: n/a Well, a bit more secure would be eval(expression, {'__builtins__': {}}, {}) or alike. Jul 18 '05 #7

 P: n/a "Adomas" writes: Well, a bit more secure would be eval(expression, {'__builtins__': {}}, {}) or alike. Don't believe this without (or even with ;-) very careful thought, anyone. Google for rexec. John Jul 18 '05 #8

 P: n/a John J. Lee wrote: "Adomas" writes:Well, a bit more secure would beeval(expression, {'__builtins__': {}}, {})or alike. Don't believe this without (or even with ;-) very careful thought, anyone. Google for rexec. John This module provides a more systematic way to set up restricted evaluation: """Restricted evaluation Main entry point: r_eval() For usage see class tests or run them using testall()""" import types import compiler import operator import sys, os # used only for testing ast = compiler.ast class Eval_Error(Exception): def __init__(self,error,descr = None,node = None): self.error = error self.descr = descr def __repr__(self): return "%s: %s" % (self.error, self.descr) __str__ = __repr__ class AbstractVisitor(object): """Standard depth-first AST walker - dispatches to methods based on Node class name""" def __init__(self): self._cache = {} # dispatch table def visit(self, node,**kw): if node is None: return None cls = node.__class__ meth = self._cache.setdefault(cls, getattr(self,'visit'+cls.__name__,self.default)) return meth(node, **kw) def default(self, node, **kw): for child in node.getChildNodes(): return self.visit(child, **kw) visitExpression = default class Eval(AbstractVisitor): """An AST walker that implements a replacement to built-in eval. See r_eval for entry point/usage. Provides hooks for managing name resolution, proxying objects, and controlling attribute access Does not implement: List Comprehensions, Generator Expressions, Lambda Ellipsis (can this be used without numpy?) """ def __init__(self, context = globals()): super(Eval,self).__init__() self.context = context # Namespace interface. Safe implementations should override these methods # to implement restricted evaluation. This implementation simply # evals the name in self.context and provides no proxying or # attribute lookup restrictions def lookup(self, objname): """Called only by visitName. Raise an exception here to prevent any direct name resolution, but note that objects may be returned by callables or attribute lookups""" return eval(objname, self.context) def getObject(self, obj): """Called by all name resolvers and by CallFunc. Provides a hook for proxying unsafe objects""" return obj def getAttribute(self,obj,attrname): """Called by visitGetattr""" return getattr(obj,attrname) # End Namespace interface # Syntax nodes follow by topic group. Delete methods to disallow # certain syntax. # Object constructor nodes def visitConst(self, node, **kw): return node.value def visitDict(self,node,**kw): return dict([(self.visit(k),self.visit(v)) for k,v in node.items]) def visitTuple(self,node, **kw): return tuple(self.visit(i) for i in node.nodes) def visitList(self,node, **kw): return [self.visit(i) for i in node.nodes] def visitSliceobj(self,node,**kw): return slice(*[self.visit(i) for i in node.nodes]) def visitEllipsis(self,node,**kw): raise NotImplementedError, "Ellipsis" # 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) # Logical Ops def visitAnd(self,node,**kw): return reduce(lambda a,b: a and b,[self.visit(arg) for arg in node.nodes]) 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 def visitOr(self,node,**kw): return reduce(lambda a,b: a or b,[self.visit(arg) for arg in node.nodes]) # Name resolution def visitGetattr(self,node,**kw): obj = self.visit(node.expr) return self.getAttribute(obj,node.attrname) def visitName(self,node, **kw): return self.lookup(node.name) def visitSlice(self,node,**kw): obj = self.visit(node.expr) objslice = obj[self.visit(node.lower):self.visit(node.upper)] return self.getObject(objslice) def visitSubscript(self,node,**kw): obj = self.visit(node.expr) subs = node.subs if len(subs) > 1: raise NotImplementedError, "Subscript must be integer or slice" assert node.flags == "OP_APPLY" return self.getObject(operator.getitem(obj,self.visit(sub s[0]))) # Callables def visitCallFunc(self,node,**kw): func = self.visit(node.node) args = node.args kwargs = self.visit(node.dstar_args) or {} posargs = [] for arg in node.args: if isinstance(arg, ast.Keyword): keyword, value = self.visit(arg) if keyword in kwargs: raise TypeError, "%s() got multiple values for keyword argument '%s'" \ % (func,keyword) kwargs[keyword] = value else: posargs.append(self.visit(arg)) posargs.extend(node.star_args or []) return self.getObject(func(*posargs,**kwargs)) def visitKeyword(self,node,**kw): return node.name, self.visit(node.expr) # Miscellaneous def visitBackquote(self, node, **kw): return repr(self.visit(node.expr)) # Function/class definition def visitLambda(self, node, **kw): raise NotImplementedError, "Lambda" # Iterator evaluations def visitGenExpr(self,node,*kw): raise NotImplemetedError, "GenExpr" def visitListComp(self,node,*kw): raise NotImplemetedError, "ListComp" # Some unexpected node type def default(self, node, **kw): raise Eval_Error("Unsupported source construct", node.__class__,node) def r_eval(source, context = None): """eval partial replacement, Does not implement: List Comprehensions, Generator Expressions, Lambda. Ellipsis """ walker = Eval(context or globals()) try: ast = compiler.parse(source,"eval") except SyntaxError, err: raise try: return walker.visit(ast) except Eval_Error, err: raise class tests(object): def run(self): """Run all the tests""" for name in dir(self): if name.startswith("test_"): getattr(self,name)() print "%s: OK" % name def test_const(self): cases = ["""[1, 2, 'Joe Smith', 8237972883334L, # comment {'Favorite fruits': ['apple', 'banana', 'pear']}, # another comment 'xyzzy', [3, 5, [3.14159, 2.71828, []]]]"""] for case in cases: assert eval(case) == r_eval(case) def test_algebra(self): """Show that r_eval matches eval on constant expressions""" cases = [ "1+2/3 * 4.0 ** 5 % 2", "(4 << 2 | 67 >> 2) ^ 0xFF", ] for case in cases: assert eval(case) == r_eval(case) def test_names(self): cases = [ "sum", "sys.modules['os']", ] for case in cases: assert eval(case) == r_eval(case) def test_calling(self): cases = [ "getattr(object, 'subclasses'.join(['_'*2]*2))", "type('Name', dict = {'a':1}, bases = (object,)).a", "type(**dict(dict = {'a':1}, name = 'Name', bases = (object,))).a" ] for case in cases: assert eval(case) == r_eval(case) def testall(): tests().run() Jul 18 '05 #9

### This discussion thread is closed

Replies have been disabled for this discussion.