Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/357.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python ValueError:使用ast.literal\u eval时字符串格式不正确_Python_Python 2.7_Python 3.x - Fatal编程技术网

Python ValueError:使用ast.literal\u eval时字符串格式不正确

Python ValueError:使用ast.literal\u eval时字符串格式不正确,python,python-2.7,python-3.x,Python,Python 2.7,Python 3.x,众所周知,使用eval()是一种潜在的安全风险,因此提倡使用 但是,在python 2.7中,当运行此示例时,它返回ValueError:malformed string: >>> ast.literal_eval("4 + 9") 而在python 3.3中,此示例按预期工作: >>> ast.literal_eval('4+9') 13 为什么它运行在Python3而不是Python2上?如何在python 2.7中修复它而不使用高风险的eval()函

众所周知,使用
eval()
是一种潜在的安全风险,因此提倡使用

但是,在python 2.7中,当运行此示例时,它返回
ValueError:malformed string

>>> ast.literal_eval("4 + 9")
而在python 3.3中,此示例按预期工作:

>>> ast.literal_eval('4+9')
13

为什么它运行在Python3而不是Python2上?如何在python 2.7中修复它而不使用高风险的
eval()
函数?

这在python 2上不起作用的原因在于它实现了
literal\u eval
。当右操作数是复数时,原始实现仅对加法和减法执行数字求值。这在语法上对于复数表示为文字是必要的

这在Python3中得到了应用,因此它支持任何类型的有效数字表达式位于加法和减法的任意一侧。然而,
literal\u eval
的使用仍然局限于加法和减法

这主要是因为
literal\u eval
应该是一个将单个常量文本(表示为字符串)转换为Python对象的函数。对于简单的内置类型,有点像向后的
repr
。不包括实际的表达式求值,而这一点在Python3中也适用,这只是其实现的一个很好的副作用

为了计算实际表达式,而不必使用
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)
正如您所看到的,这个实现非常简单。当然,它还不支持更复杂的东西,比如指数运算和一些一元节点,但是添加它们并不太困难。而且效果很好:

>>> arithmeticEval('4+2')
6
>>> arithmeticEval('4*1+2*6/3')
8

您甚至可以在以后引入更复杂的内容(例如对
sin()
之类的内容的函数调用)。

使用源代码,luke

你会在那里找到你的答案。具体来说,2.7版本有一个奇怪的限制,即BinOp的右节点是复杂的

>>> sys.version
'2.7.3 (default, Sep 26 2013, 20:03:06) \n[GCC 4.6.3]'
>>> ast.literal_eval('9 + 0j')
(9 + 0j)
>>> ast.literal_eval('0j + 9')
ValueError: malformed string

我猜2.7的意图是允许对复杂的文本进行
literal\u eval
,例如
9+0j
之类的数字,它从来没有打算进行简单的整数加法。然后在Python3中,他们增强了
literal\u eval
来处理这些情况

这是为了支持复数(因为)。例如,
1+2j
被解析器解析为一个表达式,该表达式由整数文本、加法运算和;但由于是内置类型,因此,
ast.literal\u eval
需要支持复数语法

介于2.x和3.x之间的函数用于支持以“错误的方式”写入复数,例如
1j+2
;它允许任意的加法或减法表达式,这是一个(大部分是无意的)副作用

如果要解析任意算术表达式,应该解析到语法树(使用),然后求值。

拼凑一个简单的表达式求值器并不难

假设要计算表达式(包括参数)的以下类型的表达式:

2+3
4.0^2+5*(2+3+4)
1.23+4.56-7.890
(1+2+3+4)/5
1e6^2/1e7
该示例的简化如下:


@poke答案的更新版本,允许py3.x或其他一元运算符中使用负数。例如,-3”的计算结果为-3,而不是一个错误

import ast, operator

binOps = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Mod: operator.mod
}

unOps = {
ast.USub: operator.neg
}

node = ast.parse(s, mode='eval')

def arithmetic_eval(s):
    binOps = {
    ast.Add: operator.add,
    ast.Sub: operator.sub,
    ast.Mult: operator.mul,
    ast.Div: operator.truediv,
    ast.Mod: operator.mod
    }

    unOps = {
    ast.USub: operator.neg
    }

    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))
        elif isinstance(node, ast.UnaryOp):
            return unOps[type(node.op)](_eval(node.operand))
        else:
            raise Exception('Unsupported type {}'.format(node))

    return _eval(node.body)

这绝对不是字面意思,所以我认为Python 3.3在这里是错误的。也许这是窥视孔优化的意外结果?编辑:不,它是特殊的大小写和常量折叠在
literal\u eval
中。不过,只针对加法和减法,没有乘法或除法或其他运算符。另请参见2.7中添加的奇怪行为,它似乎是为了解决这个问题。最让我困惑的是,为什么开发人员没有将python 3的
ast.literal\u eval
实现到python 2中?因为2+3不是一个literal。如果我用monkeypatch
+
做一些不同的事情会怎么样?我同意这里的2.7行为。在我看来,工作只是处理复数的一个丑陋的副作用。如果支持加法,为什么不除法、乘法或?事实上,这在3.6中最终被认为是一个bug,现在在3.7中得到了修复+1、感谢您提供有关复数的信息!没有想到加法是将它们作为文本所必需的语法的一部分。+1有趣的解决方案。。。使用
pyparser
的优点是什么?它是额外安全的还是更快的?主要优点是对输入和输出的更多控制。使用解析器与使用AST literal_eval一样安全。它将验证您的输入是否符合您的期望,并且只执行您告诉它的内容。这是一个伟大的工具。AST也是一个很棒的工具。我认为在python3中,operator.div被删除了。您可以改用operator.truediv。
"2+3"->['2', '3', '+'] = 5
"4.0^2+5*(2+3+4)"->['4.0', '2', '^', '5', '2', '3', '+', '4', '+', '*', '+'] = 61.0
"1.23+4.56-7.890"->['1.23', '4.56', '+', '7.890', '-'] = -2.1000000000000005
"(1+2+3+4)/5"->['1', '2', '+', '3', '+', '4', '+', '5', '/'] = 2.0
"1e6^2/1e7"->['1E6', '2', '^', '1E7', '/'] = 100000.0
import ast, operator

binOps = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Mod: operator.mod
}

unOps = {
ast.USub: operator.neg
}

node = ast.parse(s, mode='eval')

def arithmetic_eval(s):
    binOps = {
    ast.Add: operator.add,
    ast.Sub: operator.sub,
    ast.Mult: operator.mul,
    ast.Div: operator.truediv,
    ast.Mod: operator.mod
    }

    unOps = {
    ast.USub: operator.neg
    }

    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))
        elif isinstance(node, ast.UnaryOp):
            return unOps[type(node.op)](_eval(node.operand))
        else:
            raise Exception('Unsupported type {}'.format(node))

    return _eval(node.body)