K D*_*awG 20 python python-2.7 python-3.x
众所周知,使用eval()是一种潜在的安全风险,因此ast.literal_eval(node_or_string)促进了使用
但是在python 2.7中,它ValueError: malformed string在运行此示例时返回:
>>> ast.literal_eval("4 + 9")
Run Code Online (Sandbox Code Playgroud)
而在python 3.3中,此示例按预期工作:
>>> ast.literal_eval('4+9')
13
Run Code Online (Sandbox Code Playgroud)
为什么它运行在python 3而不是python 2?如何在不使用冒险eval()功能的情况下在python 2.7中修复它?
pok*_*oke 32
这在Python 2上不起作用的原因在于它的实现literal_eval.当righth操作数是复数时,原始实现仅对加法和减法执行数字评估.这在语法上是必要的,以便将复数表示为文字.
这在Python 3中已更改,因此它支持任何类型的有效数字表达式在加法和减法的任一侧.但是,使用literal_eval仍限于加法和减法.
这主要是因为literal_eval它应该是一个将单个常量文字(表示为字符串)转换为Python对象的函数.有点像repr简单的内置类型的倒退.实际的表达式评估不包括在内,并且这与Python 3一起工作的事实只是其实现的一个很好的副作用.
为了评估实际表达式而不必使用eval(我们不想要),我们可以编写自己的表达式评估算法,该算法对AST进行操作.这非常简单,特别是对数字的简单算术运算(例如构建自己的计算器等).我们只需将字符串解析为AST,然后通过查看不同的节点类型并应用正确的操作来评估结果树.
像这样的东西:
import ast, operator
binOps = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.div,
ast.Mod: operator.mod
}
def arithmeticEval (s):
node = ast.parse(s, mode='eval')
def _eval(node):
if isinstance(node, ast.Expression):
return _eval(node.body)
elif isinstance(node, ast.Str):
return node.s
elif isinstance(node, ast.Num):
return node.n
elif isinstance(node, ast.BinOp):
return binOps[type(node.op)](_eval(node.left), _eval(node.right))
else:
raise Exception('Unsupported type {}'.format(node))
return _eval(node.body)
Run Code Online (Sandbox Code Playgroud)
如您所见,此实现非常简单.当然它不支持更复杂的东西,如取幂和一些一元节点,但添加它并不太难.它工作得很好:
>>> arithmeticEval('4+2')
6
>>> arithmeticEval('4*1+2*6/3')
8
Run Code Online (Sandbox Code Playgroud)
你甚至可以在以后引入更复杂的东西(例如函数调用等sin()).