← 返回题目列表

Python 中 eval 和 exec 有什么区别?为什么要谨慎使用?

高频 中等 第 8 / 27 题 更新于 2026/07/27
evalexec动态执行安全

简化版

eval 用来执行表达式并返回结果,exec 用来执行语句块且没有直接返回值。它们都能动态执行字符串代码,风险很高,不能对不可信输入直接使用,否则可能造成任意代码执行。

详细版

示例:

result = eval("1 + 2")
print(result)  # 3

eval 只能执行表达式:

eval("x = 1")  # SyntaxError

exec 可以执行语句:

namespace = {}
exec("x = 1\ny = x + 2", namespace)
print(namespace["y"])  # 3

安全风险:

user_input = "__import__('os').system('rm -rf important')"
eval(user_input)

这类代码可能执行系统命令。即使传入 globalslocals 限制环境,也很难做到完全安全。

替代方案:

  • 解析 JSON 用 json.loads
  • 解析 Python 字面量用 ast.literal_eval
  • 表达式计算使用专门解析器;
  • 命令分发使用白名单字典。

完整版教学

一、eval 执行表达式

表达式是能产生值的代码片段:

1 + 2
len("abc")
{"a": 1}

eval 会执行表达式并返回结果:

value = eval("1 + 2")
print(value)  # 3

但赋值语句不是表达式:

eval("x = 1")

会报语法错误。

二、exec 执行语句块

exec 可以执行更完整的代码:

code = """
x = 1
y = x + 2
"""

namespace = {}
exec(code, namespace)
print(namespace["y"])

它适合动态生成代码、执行脚本片段等底层场景。

但普通业务代码很少需要它。看到 exec,通常要先问:能不能用普通函数、配置、映射表或插件机制替代?

三、为什么它们危险

因为字符串可以包含任意 Python 代码。

如果用户能控制字符串,就可能做危险操作:

eval("__import__('os').system('whoami')")

攻击者不一定只能读数据,也可能执行系统命令、读取环境变量、删除文件、发网络请求。

动态执行把“数据”变成了“代码”,安全边界会急剧变复杂。

四、限制 globals 和 locals 是否安全

可以传入命名空间:

eval("x + 1", {"__builtins__": {}}, {"x": 1})

这能减少可访问对象,但不代表绝对安全。Python 对象模型很复杂,某些绕过方式可能拿到不该拿的能力。

所以面对不可信输入,原则是不要用 evalexec。限制命名空间只能作为降低风险的措施,不应当作为安全沙箱。

五、常见替代方案

如果输入是 JSON:

import json
data = json.loads(text)

如果输入是 Python 字面量:

import ast
value = ast.literal_eval(text)

literal_eval 只解析字符串、数字、列表、字典、元组、布尔值、None 等字面量,不执行任意函数调用。

如果是命令分发:

commands = {
    "start": start,
    "stop": stop,
}
commands[name]()

白名单比拼接代码安全得多。

六、什么时候可以使用

合理场景通常很少:

  • 内部工具执行受信任脚本;
  • 解释器、REPL、在线代码运行器;
  • 元编程或动态生成代码;
  • 测试某些动态行为。

即使输入可信,也要把作用域限制清楚,并记录为什么不能用更简单方案。

七、常见误区与追问

易错点:evalexec 的危险不在“语法高级”,而在“把字符串当代码运行”。只要字符串来自不可信输入,就要默认它能做破坏性事情。

  • 误区:eval 只能算数学表达式,所以风险可控。 eval 执行的是 Python 表达式,表达式仍可能访问对象、调用函数、触发导入链或利用可达对象做危险操作。
  • 追问:evalexec 的核心区别是什么? eval 接收表达式并返回表达式结果;exec 接收语句块,适合执行赋值、函数定义、类定义等没有单一表达式结果的代码。
  • 误区:传空的 globals/locals 就安全了。 限制命名空间只能降低风险,不能把 CPython 变成沙箱;对象模型、内置对象恢复、第三方对象能力都可能绕开简单限制。
  • 追问:解析用户输入的列表、字典、数字应该用什么? 优先用 json.loadsast.literal_eval;后者只解析 Python 字面量,不执行任意代码。
  • 误区:后台管理功能里用 exec 就没问题。 管理入口也可能被误用、越权或被注入;如果确实需要执行动态代码,应隔离进程、权限、资源限制和审计日志。
  • 追问:什么时候可以接受动态执行? 可信内部 DSL、受控脚本平台、离线工具或教学调试可以考虑,但要明确输入来源、权限边界、超时和资源限制。

八、加强记忆

eval 执行表达式并返回值,exec 执行语句块但不直接返回结果。它们的共同风险是把字符串当代码跑,不可信输入绝对不要直接执行。遇到解析需求先想 JSON、ast.literal_eval、白名单映射或专门解析器。