Why does ast.literal_eval('5 * 7') fail?
为什么5 * 7的文字评估失败,而5 + 7的文字评估失败?
1 2 3 4 5 6 7 8 9 10
| import ast
print(ast.literal_eval('5 + 7'))
# -> 12
print(ast.literal_eval('5 * 7'))
# ->
Traceback (most recent call last):
...
ValueError: malformed node or string: <_ast.BinOp object at ...> |
文档并不能解释这一点。
在回答了这个问题之后,我发现了这个问题:得到了一个字符串的结果。
- ast.literal_eval比普通eval的威力小,因此危险性小。我想这是两次评估中较小的一次…:)
ast.literal_eval()接受评估数据中的+,因为5+2j(复数*)是有效的文字。这同样适用于-。为了保持代码的简单性,不尝试将+或-排除为二进制运算符。
不允许使用其他运算符;该函数只接受文本,而不接受表达式。
换句话说,5 + 7的工作是一个bug,但是如果不破坏对构造复数的支持就很难修复它。该实现限制了对数字、一元+和-或其他二进制运算符(因此不能使用这些运算符连接列表或产生集合差异)的操作数的使用。
另请参见几个相关的python bugtracker条目:訇25335 ast.literal_eval无法解析带前导"+"的数字,22525 ast.literal_eval()无法按文档所述执行,并且4907 ast.literal_eval未正确处理复杂数字。
*从技术上讲,2j是一个有效的文本;python将5+2j解析为int(5) binop(+) complex(0, 2),并且在实际执行加法时,仅在随后从结果生成complex(5, 2)对象。
- 谢谢你,玛金,我怀疑是这样的。这是否意味着AST是评估的结果?我希望不是。
- @Laurentlaporte:函数从字符串构建一个AST,然后使用AST构建结果。因此,只分析字符串,而不是直接执行。请参见源代码。
问题不是"为什么*不被接受",而是"为什么+被接受"。
ast.literal_eval可以解析文本,但不能解析表达式。然而,在python中,复数不是以单个文字值表示的,而是由实部和虚部加在一起组成;虚部用j表示。因此,literal_eval需要支持二进制+和-来支持复数常数,如1 + 2j或-3.4e-5 - 1.72e9j。
在许多版本中,包括python 3.5,literal_eval比它需要的宽松得多——它接受任何加减链,只要左侧和右侧都对任何数字进行计算,因此(1 + 3) + 2 + (4 - 5)仍然被解析,即使它不是由实数组成的复杂常量。+虚部
+和-不是无条件接受的:如果试图将两个列表一起添加,即使它可以解析列表文本,也会失败,并且为列表定义了添加:
1 2 3 4 5 6 7 8
| >>> ast.literal_eval('[1] + [2]')
Traceback (most recent call last):
...
ValueError: malformed node or string: <_ast.BinOp object at 0x7fdddbe785f8>
>>> ast.literal_eval('[1, 2]')
[1, 2]
>>> [1] + [2]
[1, 2] |