苦练Python第60天:json模块——让Python和JSON“无缝互译”的神兵利器

前言

大家好,我是 倔强青铜三 。欢迎关注我,微信公众号:倔强青铜三。欢迎点赞、收藏、关注,一键三连!

欢迎来到 苦练Python第60天

今天,咱们把" 数据交换界的普通话 "------JSON,彻底讲透!

用 Python 内置的 json 模块,10 分钟搞定 序列化、反序列化、自定义类型、命令行校验 全套技能。

学完它,你将彻底告别手写字符串拼接、正则解析等上古操作,优雅地完成 前后端通信、配置文件读写、单元测试断言、爬虫数据落地 等高频场景。


🎯 今日收获预览

  • 基础四件套:dump / dumps / load / loads 一次讲清
  • 进阶四件套:自定义编码器、解码钩子、紧凑/美化输出、命令行神器
  • 八大参数拆解:每个细节都配上代码
  • 实战三板斧:配置文件热加载、API 数据校验、单元测试比对
  • 常见坑位:NaN、Infinity、重复键、编码陷阱一次排雷

🧊 热身:为什么不用手撸?

灵魂三问:

  1. 你要不要处理转义 \"\u1234
  2. 你要不要兼容 datetimeDecimalUUID
  3. 你要不要让 diff 工具一眼看懂?

json 模块全帮你 封装好了 ,还自带 UTF-8 自动识别

一句话: 不要重复造轮子,除非你想加班


🗡️ 第一式:基础四件套

1. json.dumps() vs json.dump()

函数 输出目标 典型用法
json.dumps(obj) 返回 str 打日志、发网络
json.dump(obj, fp) 写入 文件对象 写文件、写 socket

一句话记忆: 带 s 的 return string,不带 s 的写文件

python 复制代码
import json, io
data = {'name': '青铜三', 'age': 18}

# 写内存
s = json.dumps(data, ensure_ascii=False)
print(s)                       # {"name": "青铜三", "age": 18}

# 写文件
with open('user.json', 'w', encoding='utf-8') as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

2. json.loads() vs json.load()

函数 输入源 典型用法
json.loads(s) str/bytes/bytearray 解析 HTTP 响应
json.load(fp) 文件对象 读配置文件

一句话记忆: 带 s 的吃字符串,不带 s 的吃文件

python 复制代码
# 从字符串解析
user = json.loads('{"name": "青铜三", "age": 18}')
print(user)                    # {'name': '青铜三', 'age': 18}

# 从文件解析
with open('user.json', encoding='utf-8') as f:
    user = json.load(f)

🧰 第二式:八大参数拆解

json.dumps() / json.dump() 共用参数

参数 作用 示例
ensure_ascii=True 非 ASCII 转义 \uXXXX 设为 False 保留中文
indent=None 美化缩进 indent=4"\t"
sort_keys=False 按键排序 回归测试必备
separators=None 分隔符 (',', ':') 最紧凑
default=None 序列化不了时回调 自定义 datetime
skipkeys=False 非 str 键跳过 True 时跳过而非抛错
allow_nan=True 支持 NaN/Infinity False 时严格模式抛错
check_circular=True 循环引用检查 False 提升速度但危险

json.loads() / json.load() 共用参数

参数 作用 示例
object_hook=None dict 变自定义对象 实现 __complex__
object_pairs_hook=None 接收有序键值对 实现重复键警告
parse_float=float 浮点解析器 换成 decimal.Decimal
parse_int=int 整数解析器 换成 float
parse_constant=None 处理 -Infinity/Infinity/NaN 自定义抛错

🧬 第三式:自定义类型完全指南

🔧 序列化 datetime

python 复制代码
import json, datetime
def dt_default(o):
    if isinstance(o, datetime.datetime):
        return o.isoformat(timespec='seconds')
    raise TypeError

log = {'time': datetime.datetime.now()}
print(json.dumps(log, default=dt_default, ensure_ascii=False))
# {"time": "2025-08-13T09:57:00"}

🔧 反序列化转回 datetime

python 复制代码
def dt_hook(d):
    if 'time' in d:
        d['time'] = datetime.datetime.fromisoformat(d['time'])
    return d
log = json.loads('{"time": "2025-08-13T09:57:00"}', object_hook=dt_hook)
print(log['time'], type(log['time']))
# 2025-08-13 09:57:00 <class 'datetime.datetime'>

🧪 第四式:命令行神器 json.tool

不用写脚本,一行命令 验证 + 美化

bash 复制代码
# 美化
$ echo '{"name":"青铜三","skills":["python","go"]}' | python -m json.tool --no-ensure-ascii
{
    "name": "青铜三",
    "skills": [
        "python",
        "go"
    ]
}

# 校验
$ echo '{1.2:3}' | python -m json.tool
Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

CLI 选项速查
--sort-keys 按键排序
--no-ensure-ascii 保留中文
--indent 4 指定缩进
--json-lines 每行一个 JSON


🎯 实战三板斧

实战 1:配置文件热加载

python 复制代码
import json, time, os

def hot_reload(path):
    last = 0
    while True:
        mtime = os.path.getmtime(path)
        if mtime > last:
            last = mtime
            with open(path, encoding='utf-8') as f:
                conf = json.load(f)
            print('配置已热更新:', conf)
        time.sleep(1)

hot_reload('config.json')

实战 2:API 单元测试断言

python 复制代码
import json, requests, unittest

class TestAPI(unittest.TestCase):
    def test_user(self):
        resp = requests.get('https://api.xxx.com/user/1')
        self.assertEqual(resp.headers['Content-Type'], 'application/json')
        data = resp.json()
        with open('tests/user_golden.json', encoding='utf-8') as f:
            golden = json.load(f)
        self.assertEqual(data, golden)

实战 3:爬虫断点续传

python 复制代码
import os, json, requests, hashlib

def download(url, meta_path='meta.json'):
    meta = {}
    if os.path.exists(meta_path):
        meta = json.load(open(meta_path))
    etag = requests.head(url).headers.get('ETag')
    if meta.get(url) == etag:
        print('👌 已下载且未更新,跳过')
        return
    # 真正下载 ...
    meta[url] = etag
    json.dump(meta, open(meta_path, 'w'), indent=2)

🧭 常见坑位汇总

场景 症状 解决
浮点精度 0.1+0.2 != 0.3 Decimal + parse_float
NaN/Infinity 不是合法 JSON allow_nan=False 或自定义 parse_constant
重复键 只保留最后一个 object_pairs_hook 捕捉
超大整数 JS 丢失精度 转字符串或 Decimal
非 str 键 自动转 str,顺序乱 sort_keys=True 回归测试

🧩 类比时间:json 和异地恋

  • json.dumps() = 把思念写成信 (str)
  • json.dump() = 把信投进邮筒 (文件)
  • json.loads() = 收到信读出来 (解析)
  • json.load() = 直接去邮筒拿信 (文件)
  • json.default() = 不会写的方言找翻译
  • json.object_hook() = 读信时脑补对象

最后感谢阅读!欢迎关注我,微信公众号:倔强青铜三

欢迎 点赞、收藏、关注,一键三连!!

相关推荐
Testopia2 小时前
AI与敏捷开发管理1:传统方法失灵?人工智能项目的新法则
人工智能·项目管理·敏捷开发·敏捷流程
孤客网络科技工作室2 小时前
Python - 100天从新手到大师:第二十七天Python操作PDF文件
开发语言·python·pdf
悬剑13143 小时前
python简易程序跑NLPIR模型
python·nlpir
wheeldown3 小时前
【Leetcode高效算法】用双指针策略打破有效三角形的个数
python·算法·leetcode
真的想不出名儿3 小时前
登录前验证码校验实现
java·前端·python
做运维的阿瑞3 小时前
Python原生数据结构深度解析:从入门到精通
开发语言·数据结构·后端·python·系统架构
Ivanqhz3 小时前
LR算法中反向最右推导(Reverse RightMost Derivation)
人工智能·算法
whltaoin3 小时前
AI 超级智能体全栈项目阶段四:学术分析 AI 项目 RAG 落地指南:基于 Spring AI 的本地与阿里云知识库实践
人工智能·spring·阿里云·向量数据库·rag
sheji34163 小时前
【开题答辩全过程】以 Web数据挖掘在电子商务中的应用研究为例,包含答辩的问题和答案
前端·人工智能·数据挖掘