simpleeval:给 Python 写个安全的表达式计算器
simpleeval:给 Python 写个安全的表达式计算器
danthedeckie/simpleeval 是一个轻量的 Python 库,斩获了 603 个 Star。


simpleeval 的核心功能是安全的表达式求值。当应用中需要让用户输入数学公式或简单逻辑表达式时,直接用 eval() 风险太高,自己写解析器又太麻烦。simpleeval 提供了一个折中方案。
它基于 Python 内置的 ast 模块解析表达式,能够精确控制哪些运算被允许、哪些被禁止。从底层杜绝了执行危险代码的可能。ast 模块将表达式解析为抽象语法树,simpleeval 在遍历这棵树时只执行白名单内的节点类型,遇到未知节点直接抛异常。
安装使用
通过 pip 安装:
pip install simpleeval
也可以直接把源文件复制到项目中。整个库只有一个文件,没有复杂的依赖关系,可以随时按需修改。
基本用法
最简用法:
from simpleeval import simple_eval
simple_eval("21 + 21")
返回 42。支持更复杂的表达式:
simple_eval("21 + 19 / 7 + (8 % 3) ** 9")
也可以传入自定义函数:
simple_eval("square(11)", functions={"square": lambda x: x*x})
如果需要执行大量求值操作,可以创建 SimpleEval 对象反复使用,避免重复解析的开销:
s = SimpleEval()
s.eval("1 + 1")
s.eval("100 * 10")
SimpleEval 对象还支持预先解析表达式并缓存语法树,之后在不同 names 下反复求值。这对于需要批量计算的场景很有帮助。
支持的运算
默认支持的运算符包括四则运算、比较运算、位运算以及 in 判断。如果不习惯 ^ 的位运算语义,可以把它替换为乘方运算。** 运算符的幂次上限默认为 4000000,防止构造出耗时过长的表达式。
表达式也支持 Python 的三元写法:
simple_eval("'equal' if x == y else 'not equal'",
names={"x": 1, "y": 2})
安全机制
simpleeval 有多层防护。字符串长度上限为 100000 字符。列表或字符串通过乘法疯狂扩展时会被拦截。
对象属性访问方面,以下划线或 func_ 开头的属性默认禁止访问。如果需要更严格的控制,可以传入 allowed_attrs 参数,将模式从黑名单切换为白名单,只放行明确允许的属性。
如果需要在表达式中暴露模块(如 os.path 的部分功能),可以使用 ModuleWrapper 进行包装,只开放指定的方法:
s = SimpleEval(names={
'path': ModuleWrapper(os.path, allowed_attrs={'exists', 'join'})
})
扩展性
SimpleEval 类可以通过继承来定制行为。例如禁用在对象上调用方法,或者添加新的运算规则。对于需要支持 dict、list、tuple 等复合类型字面量的场景,可以直接使用 EvalWithCompoundTypes 类,它也支持简单的列表推导式,并配有最大推导长度限制防止滥用。
注意事项
simpleeval 的安全性仅针对传入的表达式本身,依赖 Python 解释器正常运作。如果传递了经过 monkey-patch 的对象或包含危险代码的函数,它无法提供保护。项目作者也提醒,许多安全研究者认为在 CPython 中做沙箱化几乎不可能,使用者需要自行评估风险。不过对于常规的表达式求值场景,simpleeval 提供的安全边界已经足够。
适用场景
适合在 Web 应用中让用户输入简单公式、设置告警规则中的条件判断、或在配置文件中使用动态表达式。一个常见的例子是根据时段和音乐播放状态计算闹钟音量。另外一个典型用法是在电子表格类应用中查询单元格数据。
simpleeval 的设计初衷就是保持简单,没有堆积大量功能,但覆盖了日常开发中安全求值的大多数需求。项目自带测试套件,代码风格通过 black、isort、pylint 和 mypy 检查,维护质量有保障。
simpleeval 的设计初衷就是保持简单,没有堆积大量功能,但覆盖了日常开发中安全求值的大多数需求。项目自带测试套件,代码风格通过 black、isort、pylint 和 mypy 检查,维护质量有保障。
更多推荐


所有评论(0)