eval() 必然危险,只接受表达式,易触发 SyntaxError 或 NameError;应优先用 ast.literal_eval() 解析字面量,或改用 JSON、正则、模板引擎等安全替代方案。

直接用 eval() 执行不可信字符串,不是“可能出错”,而是“必然危险”——它会执行任意代码,抛出异常只是最温和的后果。
为什么 eval() 会触发 SyntaxError 或 NameError?
常见于用户输入、配置文件读取、日志解析等场景:字符串里混入了未加引号的变量名、不合法的运算符、缩进错误或 Python 关键字(如 if、def)被当表达式解析。
例如:eval("x + 1") 在当前作用域没有定义 x,就抛 NameError;eval("print('hi')") 因 print 是语句而非表达式,直接报 SyntaxError。
-
eval()只接受**表达式**(能返回值的代码),不能执行语句(if、for、赋值等) - 它在调用者作用域中执行,所有变量都依赖上下文,无法隔离
- 哪怕只传入
"__import__('os').system('rm -rf /')"这类字符串,也会直接执行系统命令
用 ast.literal_eval() 替代 eval() 的适用边界
如果你只是想把字符串转成基础数据结构(dict、list、str、int、float、bool、None),ast.literal_eval() 是唯一安全的内置方案。
立即学习“Python免费学习笔记(深入)”;
它只解析字面量(literal),拒绝任何函数调用、变量引用、运算符或构造器,遇到非法内容直接抛 ValueError,不会执行任意代码。
- ✅ 支持:
ast.literal_eval("[1, 'hello', {'a': True}]") - ❌ 拒绝:
ast.literal_eval("__import__('sys')")→ValueError - ❌ 拒绝:
ast.literal_eval("os.listdir('.')")→ValueError - ⚠️ 注意:
ast.literal_eval("{'a': 1, 'b': 2}")中键必须是字符串字面量,{1: 'a'}合法,但{x: 'a'}不合法(x未定义且非字面量)
需要动态计算又必须可控?用 restricted mode + compile() + exec()
极少数情况真要支持简单算术(比如配置里的 "2 * (width + height)"),可手动限制作用域和语法树节点类型,比 eval() 安全得多。
核心思路:用 compile() 解析为 AST,遍历检查是否只含白名单节点(如 BinOp、Num、Name),再用受限 exec() 执行。
- 禁止
Call、Attribute、Subscript等可能触发副作用的节点 - 提供最小化
locals字典,只包含允许使用的变量(如{"width": 100, "height": 200}) - 避免使用
globals()或locals(),防止访问内置函数 - 示例片段:
import ast<br>def safe_eval(expr, allowed_vars):<br> tree = ast.parse(expr, mode='eval')<br> for node in ast.walk(tree):<br> if not isinstance(node, (ast.Expression, ast.BinOp, ast.UnaryOp,<br> ast.Num, ast.Str, ast.Name, ast.Constant)):<br> raise ValueError("Unsafe expression")<br> return eval(compile(tree, '<string>', 'eval'), {"__builtins__": {}}, allowed_vars)
别忽略:JSON 和正则才是多数场景的真实解法
很多你以为“必须用 eval()”的地方,其实只是没选对序列化格式或解析方式。
- 配置或 API 返回的数据,应统一走
json.loads()—— 它比ast.literal_eval()更严格,也更标准 - 需要从文本中提取数值或布尔值?用正则比解析代码安全十倍:
re.search(r'"score":\s*(\d+)', text) - 模板渲染需求?用
jinja2或string.Template,它们天然沙箱隔离 - 用户自定义公式?引入专用轻量引擎(如
simpleeval库),它已内置 AST 白名单和超时机制
真正难的不是怎么让 eval() 安全,而是意识到:你根本不需要它。一旦开始写 try/except 包裹 eval(),就该停下来重审数据来源和格式设计。


















