Python 中有四种将字符串解析为对象的方法,除了常见的反序列化工具 json.loads、ujson.loads,还有 eval、ast.literal_eval 两个函数。下表概括了四者的差异:
json.loads |
ujson.loads |
eval |
ast.literal_eval |
|
|---|---|---|---|---|
| 来源 | 标准库 json |
三方库 ujson |
标准库 ast |
标准库 ast |
| 安全性 | 安全 | 安全 | 不安全 | 安全 |
| 输入 | 严格 JSON | 严格 JSON | 任意 Python 语句 | Python 字面量 |
| 支持类型 | JSON 6种类型 | JSON 6种类型 | 全部 Python 类型 | str/int/list/dict/tuple/set |
| 单引号字符串 | 不支持 | 不支持 | 支持 | 支持 |
| 性能 | 中 | 最快 | 较慢 | 较慢 |
json.loads — 标准 JSON 解析
Python 内置 json 模块用纯 Python 实现了一个递归下降 JSON 解析器。CPython 3.x 中核心部分(_json 模块)实际是 C 扩展加速,但逻辑上严格遵循 RFC 8259。
|
|
JSON 与 Python 类型映射:
| JSON | Python |
|---|---|
object |
dict |
array |
list |
string |
str |
number (int) |
int |
number (float) |
float |
true/false |
True/False |
null |
None |
注意点:JSON 的 key 必须是双引号字符串;不支持注释;NaN/Infinity 默认不支持(但可通过 parse_constant 兼容)。
ujson.loads — 超快速 JSON 解析
ujson(UltraJSON)是用 C 写的第三方 JSON 库,底层直接操作内存、避免了 CPython 对象创建的开销,对大型 JSON 文档比标准库快 2~5 倍。
|
|
注意,ujson 并非 100% 兼容 json 模块;object_hook 等钩子支持不完整;对某些边缘 Unicode 转义的处理历史上有 bug;生产环境建议锁定版本。
实际上 json.loads 在多数场景下性能已经足够,无需额外引入 ujson 增加系统复杂度。
eval() — 绝对不要用于外部输入
Python 内置函数,将字符串送入 Python 解释器的代码编译+执行流水线,和直接执行 Python 代码完全等价。
|
|
eval() 对不可信输入的任何使用场景都有替代方案,永远不要用于解析外部数据。
ast.literal_eval — 安全的 Python 字面量解析
ast.literal_eval 先用 Python 编译器将字符串解析为 AST(抽象语法树),然后只允许 AST 中出现字面量节点(Constant、List、Dict、Tuple、Set),遇到任何函数调用、名称引用等节点都会抛出 ValueError,不会执行任何代码。
|
|
经典的使用场景是解析 Python repr() 的输出,或来自可信内部系统(但非完全受信任)的配置字符串,解析 set/tuple 等 JSON 无法表达的类型。
注意:
- 对超大嵌套结构(几千层递归)有栈溢出风险(CPython 有递归深度限制),建议在解析前做大小/深度预检。
- 解析速度比
json.loads慢 5~10x,不适合高频路径。 - Python 3.2 之前
1+2会返回3(有一个算术折叠的 bug),之后修复了。 - 对于大字符串,它实际上会构建完整 AST 再遍历,内存开销比 json 高。
常见混淆场景
|
|
总结
优先用 json.loads ,不能用就 ast.literal_eval ,出现性能瓶颈就上 ujson.loads ,永远不要用 eval 。