一、前置准备
Python 的 json 模块一部分是C实现(_json),一部分是纯Python实现(json/encoder.py、json/decoder.py)
👉 读源码优先看纯Python那层,不要一头扎进C代码:
仓库:CPython:https://github.com/python/cpython/tree/main/Lib/json
核心文件:
-
Lib/json/init.py :对外API入口( dump,dumps,load,loads )
-
Lib/json/encoder.py : JSONEncoder 序列化核心(重点读)
-
Lib/json/decoder.py : JSONDecoder 反序列化核心
⚠️ 注意:底层有加速扩展 _json (C写的),默认优先使用;读源码时可以手动禁用C加速,走纯Python路径方便调试:
python
import json
json._json = None # 强制使用纯Python编码器,方便断点跟踪(仅学习用!不要业务代码这么写)
安装源码阅读环境:
-
clone cpython 仓库,用 VSCode / PyCharm 打开 Lib/json
-
断点跟踪: json.dumps(obj) 的调用链路
二、整体架构(宏观笔记模板)
plaintext
项目/模块:Python标准库 json(序列化)
核心作用:实现 Python对象 ↔ JSON字符串 的转换,遵守JSON规范
JSON支持类型:object(dict), array(list), str, int/float, True/False, None
⚠️ JSON不能直接表示:tuple、datetime、自定义类、bytes → 需要扩展
目录&职责:
-
init.py:对外暴露4个主函数,代理给 encoder/decoder
-
encoder.py:JSONEncoder,遍历对象,递归转成可JSON化的类型
-
decoder.py:JSONDecoder,词法扫描→语法解析→构建Python对象
-
scanner.py:词法分析辅助(识别数字、字符串、符号)
入口:json.dumps() #序列化;json.loads() #反序列化
主要API:
json.dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, indent=None, separators=None,
default=None, sort_keys=False, **kw)
json.loads(s, *, cls=None, object_hook=None, parse_float=None,...)
前置知识点:递归、迭代器、异常处理、Python鸭子类型
执行主干链路(dumps)
dumps() → JSONEncoder().encode(obj) → self.iterencode(obj) → _iterencode(obj, _current_indent_level) → 递归分发不同类型的处理逻辑
重点:json 不是反射遍历所有属性的黑魔法,是一套类型分发+递归遍历的算法。
三、精读:encoder.py 核心源码分析(带笔记示范)
下面摘取关键逻辑,并附上「源码阅读笔记」(就是我们上一轮说的笔记风格),省去无关兼容代码。
入口方法 encode :
python
def encode(self, o):
"""Return a JSON string representation of a Python data structure."""
if isinstance(o, str):
if self.ensure_ascii:
return self.encode_basestring_ascii(o)
else:
return self.encode_basestring(o)
chunks = self.iterencode(o, _one_shot=True)
if not isinstance(chunks, (list, tuple)):
chunks = list(chunks)
return ''.join(chunks)
📝笔记:
plaintext
模块:JSONEncoder.encode --- 序列化总入口
功能目标:接收Python对象,输出完整JSON字符串
关键函数:
- encode:外层入口;真正干活是 iterencode(生成器返回一块块字符串,最后join)
为什么用生成器 iterencode?
👉 大对象时不用一次性构造超长字符串,减少内存占用,流式输出(设计亮点❗)
主干:encode → iterencode → _iterencode(私有递归函数,真正的调度中心)
最核心私有函数 _iterencode (简化版,源码很长,我提炼骨架):
python
def _iterencode(o, _indent):
if o is None:
yield 'null'
elif o is True:
yield 'true'
elif o is False:
yield 'false'
elif isinstance(o, int):
yield str(o)
elif isinstance(o, float):
yield floatstr(o, self.allow_nan)
elif isinstance(o, str):
yield self.encode_basestring(o)
elif isinstance(o, (list, tuple)):
yield '['
first = True
for elem in o:
if not first:
yield ', '
first = False
yield from _iterencode(elem, _indent)
yield ']'
elif isinstance(o, dict):
yield '{'
first = True
items = o.items()
if self.sort_keys:
items = sorted(items)
for k,v in items:
if not first:
yield ', '
first=False
if not isinstance(k, str):
if self.skipkeys:
continue
else:
raise TypeError("key must be str")
yield from _iterencode(k, _indent)
yield ':'
yield from _iterencode(v, _indent)
yield '}'
else:
不能识别的类型,交给 default(),用户可重写!扩展点❗
if self.default is not None:
o = self.default(o)
yield from _iterencode(o, _indent)
else:
raise TypeError(f"Object of type {type(o).name} is not JSON serializable")
📝精读笔记:
plaintext
模块:_iterencode 递归分发逻辑
功能目标:**JSON序列化的核心调度器**,根据对象类型,产出对应的JSON片段
核心数据结构:没有特殊数据结构!就是利用函数递归 + 生成器yield流式输出。
👉 设计亮点(非常值得学习)
- **访问者模式思想**:不使用复杂的类继承体系,用大if‑elif做类型分发;
对标准类型内置处理;**非标准类型委托给 default() 钩子做扩展** → 这就是为什么我们可以自定义序列化!
示例:
class MyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
return obj.isoformat()
return super().default(obj)
- 使用生成器(yield)分块输出:
好处:序列化超大list/dict时,不用在内存里保存完整字符串,内存友好;dump到文件时可以一块一块write。
很多新手自己写序列化会直接拼接字符串 `s += ...`,大量字符串拷贝,效率差。
- dict的key强制只能是字符串:JSON标准约束,源码做了严格校验,可选择skipkeys跳过非法key。
边界&异常:
-
遇到不可序列化类型:若没有default钩子 → 抛TypeError(日常开发最常见报错来源!)
-
float支持nan/inf,但JSON标准不允许,由allow_nan开关控制
我的思考:
如果我自己一开始写,很可能:
-
直接递归返回str,不用生成器,大对象内存爆炸;
-
没有预留default扩展点,硬编码所有类型,以后很难扩展;
-
忘记处理tuple(json当作array),key非字符串等边界。
四、关键配套:循环引用检测(check_circular)
源码里还有一个重要逻辑:循环引用检测,比如 a=\[\], b=a; a.append(b) ,普通递归会无限递归栈溢出。
实现思路:
JSONEncoder 在 iterencode 维护一个 _markers 的id集合,每进入一个容器对象(list/dict),就把 id(obj) 放入集合;退出时移除。如果遇到已存在id → 抛 ValueError: Circular reference detected 。
📝笔记要点:
不是加锁,不是深拷贝;只是记录对象id的访问栈,非常轻量的防递归爆炸方案。很多新手写递归序列化都会漏掉循环引用保护。
五、反序列化简单看(decoder.py)
json的解码分为两层:
-
scanner:把字符串切成 token( { } : "abc" 123 true )------词法分析
-
JSONDecoder:根据token流,构建dict/list等对象------简单递归下降解析
对新手来说,编码器比解码器容易读得多,建议先吃透 encode,再看 decode,不要同时啃两边。
六、实战练习(源码阅读后的复刻作业,非常重要❗)
按照之前的方法论:读懂后,手写一个极简版序列化器 my_json.py,只支持:None、bool、int、str、list、dict;不处理转义、缩进、循环引用(先简化),然后对比标准库差距。
参考骨架:
python
def my_dumps(obj):
if obj is None:
return "null"
elif isinstance(obj, bool):
return "true" if obj else "false"
elif isinstance(obj, int):
return str(obj)
elif isinstance(obj, str):
return '"' + obj + '"' # 真实要处理 "、\ 等转义,这里省略
elif isinstance(obj, list):
items = ",".join(my_dumps(x) for x in obj)
return f"{items}"
elif isinstance(obj, dict):
parts = \[\]
for k, v in obj.items():
parts.append(f"{my_dumps(str(k))}:{my_dumps(v)}")
return "{" + ",".join(parts) + "}"
else:
raise TypeError(f"cannot serialize {type(obj)}")
if name == "main":
print(my_dumps({"name":"python","nums":1,2,3}))
做完后对比标准库:
-
缺少字符串转义( " 、 \n )
-
没有流式生成器,大对象性能差
-
没有循环引用检测
-
没有default扩展点
-
没有skipkeys、sort_keys等能力
👉 这些差异就是你从源码学到的工程细节。
七、阅读路线 & 避坑
-
不要一开始去读C版本 _json.c ,先吃透 Lib/json 纯Python逻辑;理解业务逻辑之后,有余力再看C实现做加速对比。
-
调试方式:
python
import json
json._json = None
d = {"a":1, "b":2,3}
print(json.dumps(d))
在 encoder.py 的 _iterencode 打断点,观察每一步 yield 的内容。
- 重点学习的3个工程思想:
-
生成器做流式输出,控制内存
-
default钩子实现可扩展,开闭原则
-
对象id栈检测循环引用,防栈溢出