stringExp = "2^4" intVal = int(stringExp) # Expected value: 16
这将返回以下错误:
Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: invalid literal for int() with base 10: '2^4'
我知道eval可以解决此问题,但是难道没有更好,更重要的是更安全的方法来评估存储在字符串中的数学表达式吗?
eval
Pyparsing可用于解析数学表达式。特别是,fourFn.py 显示了如何解析基本算术表达式。下面,我将fourFn重新包装为一个数字解析器类,以便于重用。
Pyparsing
fourFn.py
from __future__ import division from pyparsing import (Literal, CaselessLiteral, Word, Combine, Group, Optional, ZeroOrMore, Forward, nums, alphas, oneOf) import math import operator __author__ = 'Paul McGuire' __version__ = '$Revision: 0.0 $' __date__ = '$Date: 2009-03-20 $' __source__ = '''http://pyparsing.wikispaces.com/file/view/fourFn.py http://pyparsing.wikispaces.com/message/view/home/15549426 ''' __note__ = ''' All I've done is rewrap Paul McGuire's fourFn.py as a class, so I can use it more easily in other places. ''' class NumericStringParser(object): ''' Most of this code comes from the fourFn.py pyparsing example ''' def pushFirst(self, strg, loc, toks): self.exprStack.append(toks[0]) def pushUMinus(self, strg, loc, toks): if toks and toks[0] == '-': self.exprStack.append('unary -') def __init__(self): """ expop :: '^' multop :: '*' | '/' addop :: '+' | '-' integer :: ['+' | '-'] '0'..'9'+ atom :: PI | E | real | fn '(' expr ')' | '(' expr ')' factor :: atom [ expop factor ]* term :: factor [ multop factor ]* expr :: term [ addop term ]* """ point = Literal(".") e = CaselessLiteral("E") fnumber = Combine(Word("+-" + nums, nums) + Optional(point + Optional(Word(nums))) + Optional(e + Word("+-" + nums, nums))) ident = Word(alphas, alphas + nums + "_$") plus = Literal("+") minus = Literal("-") mult = Literal("*") div = Literal("/") lpar = Literal("(").suppress() rpar = Literal(")").suppress() addop = plus | minus multop = mult | div expop = Literal("^") pi = CaselessLiteral("PI") expr = Forward() atom = ((Optional(oneOf("- +")) + (ident + lpar + expr + rpar | pi | e | fnumber).setParseAction(self.pushFirst)) | Optional(oneOf("- +")) + Group(lpar + expr + rpar) ).setParseAction(self.pushUMinus) # by defining exponentiation as "atom [ ^ factor ]..." instead of # "atom [ ^ atom ]...", we get right-to-left exponents, instead of left-to-right # that is, 2^3^2 = 2^(3^2), not (2^3)^2. factor = Forward() factor << atom + \ ZeroOrMore((expop + factor).setParseAction(self.pushFirst)) term = factor + \ ZeroOrMore((multop + factor).setParseAction(self.pushFirst)) expr << term + \ ZeroOrMore((addop + term).setParseAction(self.pushFirst)) # addop_term = ( addop + term ).setParseAction( self.pushFirst ) # general_term = term + ZeroOrMore( addop_term ) | OneOrMore( addop_term) # expr << general_term self.bnf = expr # map operator symbols to corresponding arithmetic operations epsilon = 1e-12 self.opn = {"+": operator.add, "-": operator.sub, "*": operator.mul, "/": operator.truediv, "^": operator.pow} self.fn = {"sin": math.sin, "cos": math.cos, "tan": math.tan, "exp": math.exp, "abs": abs, "trunc": lambda a: int(a), "round": round, "sgn": lambda a: abs(a) > epsilon and cmp(a, 0) or 0} def evaluateStack(self, s): op = s.pop() if op == 'unary -': return -self.evaluateStack(s) if op in "+-*/^": op2 = self.evaluateStack(s) op1 = self.evaluateStack(s) return self.opn[op](op1, op2) elif op == "PI": return math.pi # 3.1415926535 elif op == "E": return math.e # 2.718281828 elif op in self.fn: return self.fn[op](self.evaluateStack(s)) elif op[0].isalpha(): return 0 else: return float(op) def eval(self, num_string, parseAll=True): self.exprStack = [] results = self.bnf.parseString(num_string, parseAll) val = self.evaluateStack(self.exprStack[:]) return val 你可以这样使用 nsp = NumericStringParser() result = nsp.eval('2^4') print(result) # 16.0 result = nsp.eval('exp(2^4)') print(result) # 8886110.520507872