
本文详解如何在 python 中将形如 `"x = 5"` 的字符串解析并执行为真实变量赋值,重点说明 `exec` 与 `eval` 的适用场景、局部/全局作用域限制,以及推荐的安全替代方案。
在 Python 中,将字符串(如 "X = 5")动态转为变量赋值操作,看似简单,实则涉及作用域机制、字节码编译和安全性等核心概念。直接使用 eval("X = 5") 会报错 SyntaxError: invalid syntax,因为 eval() 仅支持表达式(如 "2 + 3"),不支持赋值语句;而 exec() 才是处理语句(包括赋值、循环、函数定义等)的正确工具。
✅ 正确方式:使用 exec()(全局作用域)
在模块顶层(即全局作用域),可直接使用:
exec("X = 5")
print(X) # 输出: 5其底层原理是:全局命名空间本质上是一个可变字典(globals() 返回的 dict),exec() 会将执行结果写入该字典。你也可以显式传入作用域字典以增强可控性:
namespace = {}
exec("Y = 'hello'; Z = [1, 2, 3]", namespace)
print(namespace['Y']) # hello
print(namespace['Z']) # [1, 2, 3]⚠️ 局部作用域的陷阱:无法真正修改 locals()
在函数内部,不能通过 exec 或 eval 动态创建或修改真正的局部变量。这是因为 CPython 在编译函数时已静态确定局部变量集合,locals() 返回的只是快照(只读副本),对其修改无效:
def bad_example():
x = 1
exec("x = 999") # ❌ 不会影响真实局部变量 x
print(x) # 仍输出 1
bad_example()即使显式调用 exec(..., globals(), locals()),也无法改变这一行为——因为 locals() 在函数内返回的是不可回写(non-mutable)的拷贝。
✅ 安全可控方案:使用自定义作用域字典
若需在函数中“模拟”动态赋值并读取结果,应显式构造一个可变字典作为作用域容器:
def safe_assign(statement: str, initial_scope: dict = None) -> dict:
scope = initial_scope.copy() if initial_scope else {}
# 使用 compile 指定 'single' 模式(单条语句)提升安全性与明确性
code = compile(statement, "", "single")
exec(code, globals(), scope)
return scope
# 使用示例
result = safe_assign("name = 'Alice'; age = 30")
print(result["name"], result["age"]) # Alice 30 此方法优势明显:
- 隔离执行环境,避免污染全局或局部命名空间;
- 支持多条语句("a=1; b=2");
- 可校验、过滤输入(配合白名单或 AST 解析进一步加固);
- 易于测试与调试。
? 重要安全提醒
⚠️ 永远不要对不可信输入使用 exec 或 eval!它们可能执行任意代码,导致远程代码执行(RCE)、数据泄露等严重风险。生产环境中,应优先考虑以下替代方案:
- 使用 ast.literal_eval()(仅支持安全字面量:int, str, list, dict 等);
- 设计结构化配置(如 JSON/YAML),配合专用解析器;
- 若必须解析赋值逻辑,建议用 ast.parse() + 自定义 ast.NodeVisitor 白名单校验。
总之,exec("X = 5") 在全局可用,但在函数内需借助显式字典作用域;理解 Python 作用域的静态性与 exec 的运行时行为,是安全、可靠实现动态赋值的关键。










