关于python:为什么ast.literal_eval(’5 * 7’)会失败?

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()接受评估数据中的+,因为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.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]