Eval 函数
下面是一份 《Python eval() 最佳实践 + 安全风险 + 替代方案完整版指南》
内容涵盖:eval 是什么、能做什么、为什么危险、正确安全替代方案、实际项目中的最佳写法。
eval(expr) 会把 字符串转成 Python 表达式并执行,并返回表达式的“值”。
示例:
eval("1 + 2") # 3
eval("'a' * 3") # 'aaa'
eval("{'x': 1}") # {'x': 1}
它会:
- 解析字符串
- 当成 Python 代码执行
- 返回执行结果
⚠️ eval 与 exec 的区别:
| 函数 | 用途 |
|---|---|
| eval() | 执行表达式,有返回值 |
| exec() | 执行一段代码块,无返回值 |
如果字符串不是你自己完全控制,例如来自:
- 用户输入
- 数据库内容
- HTTP 请求参数
- 外部文件
那么运行 eval 会等于:
👉 给用户一把可以远程执行你服务器上任意 Python 代码的刀
例如:
eval("__import__('os').system('rm -rf /')")
这可以直接破坏服务器文件系统。
⚠️ 只要你不是在自己控制的代码里,eval 永远不能直接用。
以下全都不允许 eval:
- ❌ 字典转为对象
- ❌ 将字符串解析为 list/dict
- ❌ 配置文件解析
- ❌ SQL 解析
- ❌ JSON 解析
- ❌ 数学表达式计算
- ❌ 格式化模板
❌ 不要用:
eval("{'a': 1}")
✅ 安全:
import ast
data = ast.literal_eval("{'a': 1}")
literal_eval 只允许字面量(字符串、数字、列表、字典等),安全无害。
import json
json.loads('{"a": 1}')
安全库:
pip install simpleeval
from simpleeval import simple_eval
simple_eval("1 + 2 * 3")
如果必须使用 eval(极少数情况),请加上 限制变量环境:
safe = eval("1 + 2", {"__builtins__": None}, {})
只有你 完全信任表达式内容来源 时可以用 eval。
例如:
- 在你写的配置脚本里
- 在内部工具脚本中
- 在 REPL 构建动态表达式
示例:
expr = "a + b"
result = eval(expr, {}, {"a": 1, "b": 2})
这代表:
- 不允许使用 Python 内置函数
- 仅允许使用 a 和 b
- 无法执行危险代码
动态运行简单表达式
allowed = {"x": 10, "y": 20}
expr = "x * y + 5"
result = eval(expr, {"__builtins__": None}, allowed)
动态选择函数
actions = {
"sum": lambda x, y: x + y,
"mul": lambda x, y: x * y,
}
func_name = "sum"
expr = f"actions['{func_name}'](3, 5)"
result = eval(expr, {"actions": actions})
在真实项目中,99% 的 eval 使用场景都可以用安全方法替换:
| 需求 | 替代方案 |
|---|---|
| 字符串转 dict | ast.literal_eval |
| 解析 JSON | json.loads |
| 构造表达式 | 字典映射 + if/dispatch |
| 配置文件 | json / yaml / toml |
| 数学表达式 | simpleeval |
| 动态函数 | 映射表 + getattr |
- eval 会执行任意 Python 代码,本质上是不安全的。
- 绝不要在不可信数据上使用 eval。
- 使用 ast.literal_eval 或 json.loads 代替。