ValueError:使用ast.literal_eval时格式错误的字符串

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()).


eca*_*mur 6

这是为了支持复数(自问题4907).例如,1 + 2j解析器将其解析为由整数文字,加法运算和虚构文字组成的表达式; 但由于复数是内置类型,因此需要ast.literal_eval支持复数语法.

2.x和3.x之间行为变化是支持将复数编写为"错误的方式",例如1j + 2; 它允许任意加法或减法表达式的事实是(通常是非预期的)副作用.

如果要解析任意算术表达式,则应解析为语法树(使用ast.parse),使用白名单进行验证,然后进行求值.