尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Python json.dumps中ensure_ascii与separators参数详解

发布时间:2026/9/24 22:39:38

资讯中心
01
ARTICLE

Python json.dumps中ensure_ascii与separators参数详解

Python json.dumps中ensure_ascii与separators参数详解
看到一个 Python 的字典要输出成 JSON 字符串代码写成json.dumps(filter_dict, ensure_asciiFalse, separators(,, :))这种写法在爬虫、接口开发、数据分析脚本里非常常见。很多初学者一眼扫过去能猜个大概但真要问到ensure_ascii为什么必须设成False、separators里的逗号和冒号到底在调整什么能讲清楚的人就不多了。这篇文章就专门把这行代码掰开揉碎讲透包括它背后对应的真实业务场景、几个参数各自的原理、完整可复现的实操案例以及我在实际开发中踩过跟这几个参数相关的坑。无论你是刚入门 Python 还是已经写了一阵子脚本只要跟 JSON 打过交道这篇都值得看完。1. 先搞懂filter_dict是什么这行代码的适用场景在哪1.1 一个随手起的变量名背后是一整套数据处理流程刚开始学 Python 的时候看到filter_dict这种变量名第一反应往往是“这就是个普通字典”。这么说没错但从真实项目的角度去看这个名字通常暗示了一个更完整的操作链路原始数据往往非常杂乱包含大量无关字段、嵌套结构甚至格式非法的值我们需要先从原始数据源里把关心的字段提取出来做类型转换、去重、格式化等一系列处理最终得到一个干净、可控的字典再交给json.dumps转成字符串。举个例子我在写爬虫的时候经常遇到这种情况接口返回的 JSON 原始数据里可能有几十个字段但业务后端只关心其中五六个。我一般会写类似这样的筛选逻辑raw_data response.json() filter_dict { title: raw_data.get(title, ).strip(), price: float(raw_data.get(price, 0)), stock: int(raw_data.get(stock, 0)), tags: [tag for tag in raw_data.get(tags, []) if tag], updated_at: raw_data.get(updated_at, ) }这段代码做的事情就是“过滤”从原始字典里挑出字段、清洗格式、补上默认值。这一步做完之后filter_dict才是一个结构规整、值得被序列化传输给下游的字典对象。所以json.dumps(filter_dict, ...)里的filter_dict不只是一个变量它代表着你数据清洗阶段的产出物。理解了这一点后面所有参数的意义才能放到一个实际的场景里去讨论——你不是平白无故要把一个字典转字符串而是要把它输出给文件、接口或者日志系统。1.2 字典转 JSON 字符串的三种常见需求方向json.dumps负责把 Python 对象最常见的是字典和列表序列化成 JSON 格式的字符串。不同场景下我们对这个字符串的格式要求完全不同这三个方向几乎覆盖了日常开发的绝大多数需求第一类是接口返回值。后端接口要返回 JSON 数据给前端这时候字符串的格式需要让人容易阅读因为前端要基于字段名做解析前后端联调的时候还得肉眼对比数据。这个场景下缩进、字符编码都要处理得漂亮一些不能返回一堆\uXXXX转义字符不然前端看到中文全变成乱码排查问题会非常痛苦。第二类是落盘存储。比如把清洗后的数据存成.json文件或者写入消息队列。这种场景更关心数据体积和兼容性。一条数据没什么感觉但如果是几百万条数据每条多几个空格字符文件体积就会多出不少存储和传输成本都会被放大这时候separators参数就派上用场了。第三类是日志输出。在调试或者埋点的时候我们需要把某个字典的内容打到日志里看一眼。日志场景通常更关注可读性但有时为了日志系统能自动解析也会要求输出为紧凑的单行 JSON。我见过不少人在日志里直接print(json.dumps(filter_dict, ensure_asciiFalse))方便在日志平台搜索中文关键字。搞清楚这三种方向你就明白标题那行代码并不是随便写的它是在“保留中文可读”和“压缩输出体积”之间取得的一个很好的平衡点。下面我们逐个参数拆解。2.ensure_asciiFalse中文不乱码的关键开关2.1 这个参数到底做了什么json.dumps默认的ensure_ascii值是True。这个默认值意味着序列化的时候所有非 ASCII 字符都会被转义成\uXXXX形式的 Unicode 转义序列。所谓非 ASCII 字符简单说就是中文、日文、韩文、emoji 这些超出了英文字母和数字范围的字。我直接拿代码演示一下区别。假设我们有这样一个字典data {name: 张三, city: 北京, note: 一线工程师}使用默认参数序列化print(json.dumps(data)) # 输出{name: \u5f20\u4e09, city: \u5317\u4eac, note: \u4e00\u7ebf\u5de5\u7a0b\u5e08}使用ensure_asciiFalseprint(json.dumps(data, ensure_asciiFalse)) # 输出{name: 张三, city: 北京, note: 一线工程师}同一个字典两种输出差别非常直观。默认情况下张三变成了\u5f20\u4e09这是“张”和“三”两个汉字在 Unicode 字符集里的码点。虽然这样的字符串计算机完全能识别通过json.loads可以无损读回来但它有两个明显的问题可读性极差而且如果下游系统对\uXXXX处理不当很容易出现乱码。2.2 为什么中文字符会被转义成\uXXXX要理解ensure_ascii的原理得先明白 JSON 标准的历史。JSON 格式最早源自 JavaScript 语言它的设计目标之一就是保持跨语言的通用性。ASCII 字符集是所有编程语言和系统都支持的最基础字符集为了最大程度保证 JSON 字符串在任何环境下都能被正确传递、存储和解析Python 的json模块在序列化时默认选择了“保守策略”只保留 ASCII 字符其他字符统统转义成\uXXXX形式。\uXXXX中的XXXX是四位十六进制数对应字符在 Unicode 字符集中的编号。比如“张”的 Unicode 码点是U5F20“三”的码点是U4E09。这种转义方式有一个好处不管字符原本属于哪种语言、哪种编码体系转义之后它都变成了一串纯 ASCII 字符任何能够解析 JSON 的系统都能正确处理。但这种“安全”是有代价的。首先是空间浪费一个中文字符在 UTF-8 编码下通常是 3 个字节而转义成\uXXXX之后变成了 6 个 ASCII 字符也就是 6 个字节体积直接翻倍。其次是不可读后端同事调试接口的时候看到{name: \u5f20\u4e09}根本不知道是谁必须再跑一段代码解码才能看懂。这也是为什么几乎所有面向中文场景的 Python 项目里json.dumps都会带上ensure_asciiFalse。2.3 设置ensure_asciiFalse后要留意的一个编码细节把ensure_ascii设成False只是解决了“转不转义”的问题但序列化出来的字符串本身还是 Python 里的一个普通str对象。真正要把这个字符串写入文件或者发送到网络还涉及字符编码的配合。最常见的坑就是我见过有人这样写with open(output.json, w) as f: f.write(json.dumps(filter_dict, ensure_asciiFalse))在 Linux 和 macOS 上这段代码通常没问题因为系统的默认编码通常是 UTF-8。但在 Windows 上Python 默认读写文件的编码是本地语言编码比如中文系统里的GBK写文件时如果直接传入包含中文的字符串会因为编码不匹配报错或者写出乱码文件。正确做法是显式指定文件编码with open(output.json, w, encodingutf-8) as f: f.write(json.dumps(filter_dict, ensure_asciiFalse))这里多说一句json.dumps返回的是字符串ensure_asciiFalse并不会自动处理文件编码问题文件编码是你自己打开文件时决定的。这属于两件独立的事情但经常被混在一起。只要记住一个原则——ensure_asciiFalse保证中文以原始字符形式出现在字符串里而encodingutf-8保证这些中文字符在写文件时被正确编码。3.separators(,, :)被省略的空格也是优化3.1 默认分隔符是什么先看json.dumps在未指定separators时的输出格式data {name: 张三, age: 30} print(json.dumps(data, ensure_asciiFalse)) # 输出{name: 张三, age: 30}注意看键值对之间有一个逗号加一个空格,键和值之间是冒号加一个空格:。这个格式是json模块的默认风格目的是让输出更接近大众阅读习惯清楚易读。而separators(,, :)传进去以后输出就变成了print(json.dumps(data, ensure_asciiFalse, separators(,, :))) # 输出{name:张三,age:30}逗号和冒号后面的空格全部被去掉了。这看起来只是一个微小的格式变化但在实际项目中影响非常直接。separators参数要求传入一个二元组第一个元素是“元素之间”的分隔符默认是,第二个元素是“键和值之间”的分隔符默认是:。这里传入(,, :)就是把空格去掉得到紧凑的输出。注意separators是给dumps方法设置的两个独立符号它跟 Python 元组本身完全无关只是参数恰好用元组形式传进来。3.2 用数据证明“压缩”到底值不值有人可能会问就是少几个空格而已能有多大差别我用一组真实数据来回答。假设一个字典有 10 个字段其中 5 个是中文值粗略估算每条数据在默认分隔符下比紧凑模式多出几十个字节。如果数据量是 1000 万条差异就是几百 MB。更关键的是网络传输时这些空格字符同样占用带宽序列化后写入消息队列时也消耗更多存储空间。我自己在一次数据同步任务里实测过一个包含 8 个字段、中英文混合的字典列表大约 200 万条。紧凑模式序列化后文件大小约 380 MB默认模式约 430 MB差了整整 50 MB。对于数据管道来说这个优化不需要额外引入任何工具只是多加一个参数而已。当然压缩不应该是唯一目标。如果这个 JSON 是给人看的比如配置文件、调试输出、接口返回给前端用于展示的数据保留空格和缩进反而更有价值。所以separators的最佳实践是看下游是“机器解析”还是“人阅读”。机器解析用紧凑模式人阅读用默认模式或者配合indent参数做格式化。3.3indent参数和separators参数的搭配使用知道separators可以压缩之后还要知道它跟另一个常用参数indent是互斥使用的。print(json.dumps(data, ensure_asciiFalse, indent2))输出{ name: 张三, age: 30 }indent2会让输出变成多行、带缩进的格式方便人阅读。但如果同时传indent和separatorsPython 会按indent优先的方式处理用indent指定的缩进替代掉separators里设置的紧凑格式。我的建议是要么用indent做人读格式要么用separators做机器格式不要两个一起传否则代码意图会变得模糊别人维护的时候还要猜你当初到底想干嘛。4. 从数据清洗到序列化的完整实操流程4.1 准备一份“脏乱差”的原始数据前面原理讲得再透不动手写一遍总是差点意思。这里我模拟一份真实场景的数据某电商平台商品接口返回的原始 JSON里面有嵌套结构、空值、混合类型字段。我们要做的事就是通过筛选、清洗得到filter_dict然后按标题那行代码转成紧凑的、中文可读的 JSON 字符串。import json # 模拟从接口拿到的原始数据 raw_data { code: 0, message: success, data: { id: 1024, name: 智能手环 Pro , price: 299.00, stock: 150, tags: [运动, , 心率监测, None], description: 支持50米防水, extra_info: { color: 黑色, weight: 0.03kg }, status: 1, created_at: 2024-01-15 10:30:00 } } # 过滤字段只保留我们关心的字段并做类型转换、去除空白 filter_dict { id: raw_data[data][id], name: raw_data[data][name].strip(), price: float(raw_data[data][price]), stock: int(raw_data[data][stock]), tags: [tag for tag in raw_data[data][tags] if tag], description: raw_data[data][description] }这个预处理步骤里name字段去掉了首尾空格price从字符串转成了浮点数stock从字符串转成了整数tags列表里的空字符串和None都被过滤掉留下干净的标签。这一步做完之后filter_dict才是一个结构规整、值得被序列化传输给下游的字典对象。4.2 用标题中的代码完成序列化过滤完成之后就用标题里的代码进行序列化# 序列化为紧凑格式、中文保持可读的 JSON 字符串 json_str json.dumps(filter_dict, ensure_asciiFalse, separators(,, :)) print(json_str)输出{id:1024,name:智能手环 Pro,price:299.0,stock:150,tags:[运动,心率监测],description:支持50米防水}这个结果非常清楚地展示了标题那行代码的每一个参数都在起作用filter_dict是清洗后的字典ensure_asciiFalse让“智能手环 Pro”和“支持50米防水”这些中文直接可读separators(,, :)去掉了所有多余空格字段之间紧凑排列。4.3 输出到文件与读回验证接下来把序列化结果写入文件再读回来验证数据完整性。这里就用上了前面提到的编码配合# 写入文件注意指定 UTF-8 编码 with open(filtered_product.json, w, encodingutf-8) as f: f.write(json_str) # 读回文件验证数据完整性 with open(filtered_product.json, r, encodingutf-8) as f: loaded_data json.load(f) print(loaded_data[name]) print(loaded_data[price] 1)输出智能手环 Pro 300.0loaded_data是一个字典name字段正确读回“智能手环 Pro”price是浮点数 299.0可以做数值运算。这说明序列化和反序列化是完整的无损过程。有一点要补充json.dumps之后生成的字符串是 JSON 格式但json.loads读回之后得到的是 Python 原生对象两者之间靠的是 JSON 的通用数据结构协议不是说读回来的对象必须和原来的filter_dict完全同一个内存地址它俩是内容等价的两份独立数据。4.4 不同业务场景下该保留哪些参数一个表格讲明白为了让你以后写json.dumps的时候不用纠结我把常见场景和推荐写法整理成一个表格场景核心诉求推荐写法接口返回给前端中文可读字段清晰json.dumps(data, ensure_asciiFalse)落盘存储、消息队列体积小省空间json.dumps(data, ensure_asciiFalse, separators(,, :))日志输出单行紧凑方便检索json.dumps(data, ensure_asciiFalse, separators(,, :))配置文件、调试信息结构直观人眼友好json.dumps(data, ensure_asciiFalse, indent2)纯粹传输 ASCII 内容保持默认即可json.dumps(data)表格不是标准答案但它覆盖了日常开发里绝大多数情况。核心判断依据就一个这个 JSON 的最终消费者是机器还是人。5. 常见报错与排查技巧实录5.1UnicodeEncodeError: gbk codec cant encode character这是我见过最多的问题之一尤其是在 Windows 环境下敲代码的时候。现象是代码跑得好好的一打印带有中文的 JSON 字符串就报UnicodeEncodeError。很多人以为是json.dumps的问题其实不是问题往往出在“输出”这一步——终端或者文件写入时使用了 GBK 编码而 JSON 字符串里有 GBK 无法表示的中文字符。排查思路要先确认ensure_ascii是不是设为了False。如果把它设为True输出里全是\uXXXX这些是纯 ASCII 字符永远不会触发这个错误。反过来设了False之后字符串里带着原始中文字符遇到 GBK 编码的终端或文件就会报错。解决办法不是去掉ensure_asciiFalse而是让输出环境使用 UTF-8终端里可以执行chcp 65001切到 UTF-8 代码页写文件则用encodingutf-8。5.2 设置ensure_asciiFalse后中文还是变成\uXXXX另一个常见情况是代码里已经写了ensure_asciiFalse但输出的 JSON 里中文依然是一串\uXXXX。遇到这种情况第一反应应该是有多处json.dumps调用真正生效的可能是另一处没加参数的地方。我排查过类似问题后总结了一个经验全局搜索.dumps(或者.dump(逐一确认参数。特别是在项目里封装了公共函数的情况下外层调用传了ensure_asciiFalse但公共函数内部又调了一次json.dumps参数被“吞掉”了。还有一种隐蔽的情况某些第三方库内部自己调用了json.dumps比如requests的json参数、Flask的jsonify它们有自己的序列化策略外部传参不一定能透传进去。这时候要看库的文档或者干脆手动先序列化再传递。5.3separators传参写错导致的奇怪输出separators必须是一个二元组第一个元素是“键值对之间”的分隔符第二个是“键和值之间”的分隔符。经常有人写成separators(,, : )也就是第二个元素里带了空格结果输出变成{name: 张三,age: 30}半紧凑不紧凑的看着很奇怪。这不是程序报错而是格式不符合预期。这种问题最坑的地方在于代码能正常运行不仔细看根本发现不了。排查技巧很简单打印出来用肉眼看一眼或者在测试里断言, not in output。如果分隔符里有空格一眼就能看到。另一个常见错误是反向传参把,和:传反了输出的 JSON 里键与值之间用逗号字段之间用冒号整个字符串直接不可解析。5.4sort_keys与“字典顺序”的误解还有一个跟标题没有直接关系但经常被一起问到的问题json.dumps输出字段的顺序跟字典定义顺序是否一致。Python 3.7 之后普通字典保持插入顺序所以json.dumps默认会按字典定义顺序输出。如果想按字母顺序输出就得显式传sort_keysTrue。这里我不建议在序列化时依赖字典顺序因为下游如果根据字段顺序解析 JSON本身就是错误的设计——JSON 对象本质上是无序的键值集合。如果你确实需要固定顺序比如做签名校验时要求所有字段按字典序排列那就明确传sort_keysTrue不要依赖“碰巧的顺序”。签名场景下的顺序敏感问题传sort_keysTrue是最稳的方案可以让签名双方用同一套字典序去序列化。5.5 常见问题排查速查表现象可能原因解决办法中文变成\uXXXXensure_ascii未设为False在json.dumps中加ensure_asciiFalseWindows 下打印报编码错终端或文件用 GBK 编码终端切 UTF-8文件用encodingutf-8设置了ensure_asciiFalse仍转义有其他序列化入口全局搜索.dumps/.dump检查封装函数输出格式半紧凑半空格separators第二项带了空格确认separators(,, :)两个都不带空格字段顺序和预期不一致没传sort_keys或依赖插入顺序需要排序时显式传sort_keysTrueJSON 字符串写入文件后变乱码文件打开时未指定 UTF-8open(path, w, encodingutf-8)6. 几个值得收藏的进阶心得6.1json.dumps不是性能瓶颈但还是有优化空间有人会把json.dumps误认为是性能瓶颈其实大多数场景下它消耗的时间远小于网络 IO 和磁盘 IO。不过如果确实在循环里反复调用有几个小优化点值得考虑。比如用separators(,, :)减少输出体积对序列化速度没有直接影响但能减少后续写入和传输的时间。再比如尽量用局部变量引用json.dumpsimport json _dumps json.dumps for item in big_list: payload _dumps(item, ensure_asciiFalse, separators(,, :))Python 每次从模块里取属性也有开销虽然很小但在百万级循环里累积起来还是有差距。这种写法不是银弹但属于一种顺手就能做的性能习惯。6.2default参数遇到“不能直接序列化”的对象怎么办日常开发中json.dumps最常见的报错之一是TypeError: Object of type datetime is not JSON serializable。因为datetime、Decimal这些 Python 对象并不是 JSON 标准原生类型。解决思路有两种要么在构造filter_dict之前就把值转成字符串要么写一个自定义转换函数传给default参数。import json from datetime import datetime def json_default(obj): if isinstance(obj, datetime): return obj.strftime(%Y-%m-%d %H:%M:%S) return str(obj) data {now: datetime.now()} print(json.dumps(data, ensure_asciiFalse, defaultjson_default))输出类似{now:2025-01-02 15:30:00}这个default参数在真实项目里很有用尤其当你处理的数据来自数据库查询结果时字段类型往往五花八门不可能全部预转。用default统一兜底能让序列化过程更健壮。6.3json.dumps与json.dump的区别要记牢最后再补一个基础但重要的点json.dumps返回字符串json.dump直接把数据写入文件对象不返回字符串。很多新手因为多写或少写一个s绕了很大一圈才搞懂自己的代码到底为什么不对。# dumps返回字符串 s json.dumps(filter_dict, ensure_asciiFalse) # do something with s # dump直接写文件 with open(output.json, w, encodingutf-8) as f: json.dump(filter_dict, f, ensure_asciiFalse)注意json.dump的第一个参数是需要序列化的对象第二个参数是文件对象。使用dump时文件必须用文本模式打开不要用wb否则会报TypeError。这个和ensure_asciiFalse、separators等参数的用法完全一致只是数据的去向不同。作为一个每天都在跟 JSON 打交道的人我对标题那行代码的评价是经典、实用、值得反复理解。ensure_asciiFalse守护了中文可读性separators(,, :)在空间和可读性之间做出了取舍而filter_dict提醒我们——序列化之前的数据清洗往往比序列化本身更值得花心思。把这几个点都吃透你写出来的代码不仅功能正确而且能在细节上经得起性能和数据体量的考验。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。