我如何(轻松地)获取一个字符串,例如"sin(x)*x^2"用户可能在运行时输入的字符串,并生成一个可以评估任何值的Python函数x?
是否有Python的数学表达式解析器+评估器?
我不是第一个提出这个问题的人,但答案通常指向eval().例如,人们可以这样做:
>>> safe_list = ['math','acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', 'degrees', 'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log', 'log10', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'abs']
>>> safe_dict = dict([ (k, locals().get(k, None)) for k in safe_list ])
>>> s = "2+3"
>>> eval(s, {"__builtins__":None}, safe_dict)
5
Run Code Online (Sandbox Code Playgroud)
但这不安全:
>>> s_badbaduser = """
... (lambda fc=(
... lambda n: [
... c for c in
... ().__class__.__bases__[0].__subclasses__()
... if c.__name__ …Run Code Online (Sandbox Code Playgroud) 原始问题:
在python Web服务器上执行数学用户代码,最简单的安全方法是什么?
由于需要Python的一小部分,我目前的方法是通过遍历Python的抽象语法树将允许的语法列入白名单.功能和名称得到特殊待遇; 只允许明确列入白名单的函数,并且只允许使用未使用的名称.
import ast
allowed_functions = set([
#math library
'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh',
'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf',
'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod',
'frexp', 'fsum', 'gamma', 'hypot', 'isinf', 'isnan', 'ldexp',
'lgamma', 'log', 'log10', 'log1p', 'modf', 'pi', 'pow', 'radians',
'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc',
#builtins
'abs', 'max', 'min', 'range', 'xrange'
])
allowed_node_types = set([
#Meta
'Module', 'Assign', 'Expr',
#Control
'For', 'If', 'Else',
#Data
'Store', 'Load', 'AugAssign', 'Subscript',
#Datatypes …Run Code Online (Sandbox Code Playgroud) 好.我知道 的 专家 都 讲,你不应该永远使用Python的eval()不受信任的数据,直到永远.我并不比世界其他地方更聪明,甚至不应该尝试这个.但!无论如何,我要去.
我的基本问题是我正在编写一个小计算器评估程序,它将使用python语法的一个子集进行不可信的输入.我知道:使用ply或pyparsing并编写解析器然后我们去.通过传递全局和本地人来解决这个问题是eval()不行的.
我所看到的所有方法(并且一直保持冷漠)试图列举邪恶.在这里,我试图枚举好 - 得到一个AST,只允许几个节点类型,然后验证任何调用是否属于一组白名单函数之一.这是一个迷你实现(和要点):
import ast
import math
SAFE_FX = {
'exp': math.exp,
}
SAFE_NODES = set(
(ast.Expression,
ast.Num,
ast.Call,
ast.Name,
ast.Load,
ast.BinOp,
ast.Add,
ast.Sub,
ast.Mult,
ast.Div,)
)
class CleansingNodeVisitor(ast.NodeVisitor):
def generic_visit(self, node):
if type(node) not in SAFE_NODES:
raise Exception("%s not in SAFE_NODES" % type(node))
super(CleansingNodeVisitor, self).generic_visit(node)
def visit_Call(self, call):
if call.func.id not in SAFE_FX:
raise Exception("Unknown function: %s" % …Run Code Online (Sandbox Code Playgroud)