不能直接用eval,因为它会执行任意Python表达式,包括危险操作如os.system;ast.literal_eval仅安全解析str、int、list等无副作用字面量,不支持函数调用或运算符,且对语法格式极其严格。

为什么不能直接用 eval
因为 eval 会执行任意 Python 表达式,包括调用函数、访问全局变量、甚至执行 os.system("rm -rf /") 这类危险操作。哪怕输入看起来“只是个列表”,攻击者也能通过构造字符串绕过简单过滤,比如:eval("[1, 2, __import__('os').system('id')]") —— 这根本不是“安全求值”,而是远程代码执行。
ast.literal_eval 能处理哪些字面量
它只允许解析有限的、无副作用的 Python 字面量结构,包括:str、bytes、int、float、complex、list、tuple、dict、set、frozenset、None、True、False。不支持变量名、函数调用、运算符(如 +、**)、lambda、条件表达式等。
常见可用示例:
import ast
ast.literal_eval("[1, 2, 'hello']")
ast.literal_eval("{'a': 3.14, 'b': None}")
ast.literal_eval("(1, 2, 3)")
ast.literal_eval("{'x', 'y'}")
以下都会抛出 ValueError:
立即学习“Python免费学习笔记(深入)”;
ast.literal_eval("os.listdir('.')")ast.literal_eval("[1, 2] + [3]")-
ast.literal_eval("{'key': value}")(value是未定义变量) ast.literal_eval("1 if True else 2")
遇到嵌套结构或非标准格式怎么办
ast.literal_eval 对格式极其严格:字符串必须是合法的 Python 字面量语法,不能多空格、不能缺引号、不能用单引号包双引号内容却没转义。例如:"{'name': 'O'Reilly'}" 会失败(单引号冲突),而 {"name": "O'Reilly"} 是合法的 JSON,但不是合法的 Python 字面量(键必须是字符串字面量,且需引号包裹)。
实际建议:
- 若数据来自外部(如 API、配置文件),优先用
json.loads替代——它更宽松(支持双引号键、无尾逗号)、有明确规范、且同样不执行代码 - 若必须处理用户输入的类 Python 格式,先用正则或字符串预处理修复明显问题(如把
true→True),但注意这容易引入新漏洞,不推荐 - 对含注释、YAML 风格或带变量插值的字符串,
ast.literal_eval完全不适用,应换用专用解析器(如pyyaml或tomllib)并关闭执行能力
性能和兼容性差异小,但错误类型要留意
ast.literal_eval 比 eval 略慢(因语法树遍历更严格),但日常使用几乎无感;Python 2.6+ 全版本支持,无兼容问题。
关键区别在于异常类型:
-
eval解析失败通常抛SyntaxError或运行时异常(如NameError) -
ast.literal_eval只抛ValueError(输入非法)或MemoryError(超大嵌套结构)
所以捕获异常时别写 except SyntaxError:,否则会漏掉所有安全校验失败的情况。
真正难处理的是“看起来像字面量,实则带陷阱”的边界情况——比如超深嵌套耗尽栈空间,或构造极大字符串触发内存限制。这些不会报错,但会让服务卡住或崩溃。


















