import ast
import re
from _ast import *
_NUM_TYPES = (int, float, complex)
def literal_eval(node_or_string):
"""
Safely evaluate an expression node or a string containing a Python
expression. The string or node provided may only consist of the following
Python literal structures: strings, bytes, numbers, tuples, lists, dicts,
sets, booleans, and None.
"""
if isinstance(node_or_string, str):
node_or_string = ast.parse(node_or_string, mode='eval')
if isinstance(node_or_string, Expression):
node_or_string = node_or_string.body
def _convert(node):
if isinstance(node, Constant):
return node.value
elif isinstance(node, (Str, Bytes)):
return node.s
elif isinstance(node, Num):
return node.n
elif isinstance(node, Tuple):
return tuple(map(_convert, node.elts))
elif isinstance(node, List):
return list(map(_convert, node.elts))
elif isinstance(node, Set):
return set(map(_convert, node.elts))
elif isinstance(node, Dict):
return dict((_convert(k), _convert(v)) for k, v
in zip(node.keys, node.values))
elif isinstance(node, NameConstant):
return node.value
elif isinstance(node, UnaryOp) and isinstance(node.op, (UAdd, USub)):
operand = _convert(node.operand)
if isinstance(operand, _NUM_TYPES):
if isinstance(node.op, UAdd):
return + operand
else:
return - operand
elif isinstance(node, BinOp) and isinstance(node.op, (Add, Sub)):
left = _convert(node.left)
right = _convert(node.right)
if isinstance(left, _NUM_TYPES) and isinstance(right, _NUM_TYPES):
if isinstance(node.op, Add):
return left + right
else:
return left - right
elif isinstance(node, BinOp) and isinstance(node.op, (Mult, Div)):
left = _convert(node.left)
right = _convert(node.right)
if isinstance(left, _NUM_TYPES) and isinstance(right, _NUM_TYPES):
if isinstance(node.op, Mult):
return left * right
else:
return left / right
elif isinstance(node, Compare) and all([isinstance(x, (Lt, LtE, Gt, GtE, Eq, NotEq)) for x in node.ops]):
left = _convert(node.left)
comparators = [_convert(comparator) for comparator in node.comparators]
if len(comparators) == len(node.ops):
for i in range(len(node.ops)):
op = node.ops[i]
comparator = comparators[i]
if isinstance(left, _NUM_TYPES) and isinstance(comparator, _NUM_TYPES):
if isinstance(op, Lt):
result = (left < comparator)
elif isinstance(op, LtE):
result = (left <= comparator)
elif isinstance(op, Gt):
result = (left > comparator)
elif isinstance(op, GtE):
result = (left >= comparator)
elif isinstance(op, Eq):
result = (left == comparator)
elif isinstance(op, NotEq):
result = (left != comparator)
else:
break
left = comparator
if not result:
return False
else:
return True
elif isinstance(node, BoolOp) and isinstance(node.op, (And, Or)):
values = node.values
if len(values) == 2:
left = _convert(values[0])
right = _convert(values[1])
if (isinstance(left, _NUM_TYPES) or isinstance(left, (NameConstant, Constant))) and (isinstance(right, _NUM_TYPES) or isinstance(right, (NameConstant, Constant))):
if isinstance(node.op, And):
return left and right
elif isinstance(node.op, Or):
return left or right
elif isinstance(node, UnaryOp) and isinstance(node.op, (Not, )):
value = _convert(node.operand)
if isinstance(value, _NUM_TYPES) or isinstance(value, (NameConstant, Constant)):
return not value
raise ValueError('malformed node or string: ' + repr(node))
return _convert(node_or_string)
python中eval功能十分强大, 但是同时安全漏洞也很明显, 通过__import__('os').system方法, 可以通过字符串eval方式执行任意cmd, 所以通常不采用eval去做功能, 但有些时候, 一些需求需要用到动态计算, 如果单纯通过模型间关系去做几乎不可能, 如果通过解析引擎去动态计算, 构建引擎的复杂度本身就超过了计算的复杂度, 通过资料的查找, 可以通过ast的literal_eval的方式去执行计算, 但是改方法本身只支持加减运算, 通过对源码copy修改后, 即可放开对乘除的计算, 代码如上
2023/1/3更新
增加了对小于、大于、等于、不等于、且、或、非等判断的支持