
这周连续撞上两桩怪事:先是为双快照做内容指纹,两份一模一样的数据哈希死活对不上;后来又发现紧凑序列化能省下十几个字节,而同事的代码里怎么改都省不出来。追到底,两桩都是同一个源头——json.dumps 的默认参数。它有四个默认值,每一个都曾在真实数据上咬过人。这篇把它们逐一实测(Python 3.13.12),账单如下。

文章目录
-
- 一、ensure_ascii:中文字节的 46% 税
- 二、sort_keys:指纹对不齐的真凶
- 三、separators:默认值里藏着两个空格
- 四、allow_nan:一个不算 JSON 的合法输出
- 五、round-trip 不是恒等
- 六、给指纹场景立一条铁律
- 七、30 秒带走版
一、ensure_ascii:中文字节的 46% 税
默认 ensure_ascii=True 会把所有非 ASCII 字符转成反斜杠 u 开头的转义序列。实测对象是一个最简单的两键字典:键为城市、值为香港,再加一个人数计数。默认写法序列化出来 57 字节,关掉转义后 39 字节——同样的数据,多付 46%。账目很清楚:字典里的 6 个汉字按 UTF-8 本是每个 3 字节,转义后变成每个 6 个 ASCII 字符,光这一项就净多 18 字节。
这不只是难看:转义后的串在日志里不可读(排查时要人肉反解)、字节统计翻倍、与前端核对字段时长度对不上。对外接口建议保持默认(最大兼容老客户端),日志与本地文件建议关掉——ensure_ascii=False 加上文件写入时显式指定 utf-8 编码,两件事缺一不可,漏了后者会直接抛编码异常。
二、sort_keys:指纹对不齐的真凶

更隐蔽的一个:dict 的序列化顺序等于插入顺序。同一个内容、两种构建顺序,产出的字节串不同,sha1 指纹实测分别为 b6ebf63d155d 与 bfb36f31854e——值相等,指纹不同。我上周的双快照校验就是这样误报的:同一接口两次抓取,对端在两次响应之间调整了字段顺序,我的指纹却拿它们当两份数据。
修法一行:序列化时加 sort_keys=True——实测加排序后两次哈希精确一致。凡是用序列化结果做指纹、哈希、去重的场景,这个参数不是可选项。它解决不了嵌套列表内部的顺序差异(那是数据本身的问题),但能把「键序」这一整类误报清零。
三、separators:默认值里藏着两个空格
默认分隔符在逗号和冒号后面各跟一个空格。实测一个四键小字典:默认 28 字节,紧凑写法 23 字节。示例越小差距越不起眼,但它是按键值对固定征收的:上万条记录的导出文件里,这就是稳稳的一成体积。配合关闭 ascii 转义,导出类任务的体积能省下两成以上。反过来也要提醒:对外接口不要为省字节改分隔符——紧凑与否是合法的 JSON 两面,但对接方的字节数校验、签名计算若基于默认格式,改动会破坏对账。
四、allow_nan:一个不算 JSON 的合法输出
最意外的一个。默认 allow_nan=True 下,含正负无穷与非数的字典照样能序列化,输出里出现 NaN、Infinity 这样的字面量——而 JSON 规范(RFC 8259)里根本没有这些字面量,严格解析器(包括浏览器里的 JSON.parse)拿到就会抛错。实测更迷惑的是:标准库自己的解析器居然能把 NaN 原样读回——这是 CPython 自留的非标准后门,为的是自家 round-trip 无损。
换句话说:Python 写出的「JSON」,Python 自己读得懂,全世界都读不懂。实测这个后门是双向的——写进去的 NaN 能原样读回,但这只在本语言内成立;任何跨语言的链路(浏览器、移动端、严格解析器)都会在同一个字面量上断掉,而且报错的人看到的是一份「看起来完全正常」的输出。带非数的实测量要出网,先在序列化前清洗成空值或显式标记字符串;指望默认参数替你把关,它只会沉默地放行。
五、round-trip 不是恒等

把四条静默变形放在一起看:元组过一趟变列表;整数键变字符串键(取值方式得跟着改);datetime 对象要么先转字符串、要么直接抛错——而字符串化会把时区标签剥掉,这正是昨天那篇 utcnow 漂移的下游事故。读回等于写出,只对 JSON 原生类型成立;跨过去一趟,形状、键型、标签都可能悄悄换掉。任何依赖 round-trip 保真的代码,都该有类型断言兜底。
六、给指纹场景立一条铁律
综合这一周的两次事故,给所有「用序列化结果做比对」的场景立一条三件套铁律:排序(sort_keys=True)+ 关转义(ensure_ascii=False)+ 紧凑分隔符,三个参数同时固定,指纹才有可比性——只固定其中一个,另外两个默认值随时会把比对结果搅浑。顺带一提,探表式调试时先打印序列化结果的前 60 个字符再算哈希:转义符、空格、键序这三类问题,肉眼一眼可见,哈希只会告诉你「不相等」。
七、30 秒带走版
参考链接
本文实验环境:Python 3.13.12 标准库;字节数与哈希值均为本机实测,哈希示例值随内容不同而不同,方法可直接复现。

网硕互联帮助中心






评论前必须登录!
注册