Python-计算字符串中的数学表达式

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可以解决此问题,但是难道没有更好,更重要的是更安全的方法来评估存储在字符串中的数学表达式吗?

回答:

Pyparsing可用于解析数学表达式。特别是,fourFn.py 显示了如何解析基本算术表达式。下面,我将fourFn重新包装为一个数字解析器类,以便于重用。

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

以上是 Python-计算字符串中的数学表达式 的全部内容, 来源链接: utcz.com/qa/435664.html

回到顶部