众所周知,使用eval()
是一种潜在的安全风险,因此推广使用^{}
但是在python 2.7中,当运行以下示例时,它返回ValueError: malformed string
:
>>> ast.literal_eval("4 + 9")
而在Python3.3中,此示例的工作方式与预期一致:
>>> ast.literal_eval('4+9')
13
为什么它运行在python 3而不是python 2上?如何在python 2.7中修复它而不使用有风险的eval()
函数?
Tags:
这在Python 2上不起作用的原因在于它实现了
literal_eval
。原始实现仅在右操作数为复数时对加法和减法执行数值计算。这在语法上是必要的,复数要表示为一个文字。这是Python 3中的was changed,因此它支持任何类型的有效数字表达式位于加减运算的任何一边。然而,
literal_eval
的使用仍然限于加法和减法。这主要是因为
literal_eval
应该是一个函数,它将单个常量文本(表示为字符串)转换为Python对象。有点像简单内置类型的向后repr
。实际的表达式求值不包括在内,而这与Python3一起工作的事实只是它实现的一个很好的副作用。为了计算实际表达式,不必使用
eval
(我们不想这样做),我们可以编写自己的表达式计算算法,该算法在AST上运行。这是非常简单的,特别是对数字进行简单的算术运算(例如构建自己的计算器等)。我们只需将字符串解析为AST,然后通过查看不同的节点类型并应用正确的操作来计算结果树。像这样的:
如您所见,这个实现非常简单。当然,它还不支持更复杂的东西,比如指数运算和一些一元节点,但是添加它并不太困难。而且效果很好:
以后甚至可以引入更复杂的内容(例如,函数调用
sin()
)。这是为了支持复数(因为issue 4907)。例如,
1 + 2j
被解析器解析为一个表达式,该表达式由整数文本、加法运算和imaginary literal组成;但是由于complex numbers是一个内置类型,因此ast.literal_eval
需要支持复数语法。介于2.x和3.x之间的change in behaviour是为了支持以“错误的方式循环”写入复数,例如
1j + 2
;它允许任意的加法或减法表达式是一个(大多数是无意的)副作用。如果要解析任意算术表达式,应该解析到语法树(使用^{} )、verify it with a whitelist,然后计算。
利用消息来源,卢克!
^{} ^{}
你会在里面找到答案的。具体来说,2.7版本对line 70有一个奇怪的限制,即BinOp的右节点是复杂的。
我猜2.7的目的是允许复杂文本的
literal_eval
,例如9 + 0j
这样的数字,它从来没有打算做简单的整数加法。然后在python 3中,他们增加了literal_eval
来处理这些情况。相关问题 更多 >
编程相关推荐