eval本身不声明变量,而是通过执行赋值语句在指定globals/locals中写入;推荐用exec配合自定义scope字典实现安全变量注入,或优先使用ast.literal_eval、simpleeval等更安全的替代方案。

eval 可以在指定上下文中动态声明变量,但必须注意:它本身不直接“声明变量”,而是通过执行赋值语句(如 "x = 10")在目标命名空间中写入变量。关键在于控制 globals 和 locals 参数,避免污染当前作用域。
用自定义字典作为 locals 实现安全变量注入
这是最常用也最可控的方式——把变量名和值预先组织进一个字典,再让 eval 在该字典中执行赋值语句:
- 定义空字典 scope = {} 作为独立执行环境
- 构造赋值字符串,例如 code = "a = 5; b = a * 2; c = [a, b]"
- 调用 exec(code, {}, scope)(注意这里用 exec,因为含多条语句;eval 仅支持单个表达式)
- 执行后,scope 中就包含 {'a': 5, 'b': 10, 'c': [5, 10]}
用 globals 控制函数与常量可见性
当表达式需调用特定函数或引用固定常量时,可通过 globals 字典显式提供,隔离外部环境:
- 准备受限的全局环境:safe_globals = {"len": len, "max": max, "PI": 3.14159}
- 用户输入表达式:user_expr = "len('hello') * PI"
- 执行:result = eval(user_expr, {"__builtins__": {}}, safe_globals)
- 这样既支持必要功能,又禁用了 __import__、open 等危险内置函数
动态生成变量名并赋值(谨慎使用)
根据运行时数据构造变量标识符,适用于配置解析或模板填充场景:
- 假设从 JSON 读取字段名 field = "user_age" 和值 value = 28
- 拼接赋值语句:stmt = f"{field} = {value}"
- 在干净环境中执行:scope = {}; exec(stmt, {}, scope)
- 后续可统一通过 scope["user_age"] 访问,不污染模块级变量
替代 eval/exec 的更安全方案
若目标只是解析结构化数据或简单公式,优先考虑更受限的替代方法:
- 解析字面量(列表、字典、数字等):用 ast.literal_eval(),它只允许安全的 Python 字面量,拒绝函数调用和变量引用
- 计算数学表达式:用 simpleeval 或 asteval 这类专用库,可白名单控制可用函数和操作符
- 映射式变量绑定:用字典代替动态变量名,例如 data["user_age"] = 28,逻辑清晰且无执行风险











