—把所有文字放进同一张码表,给每个字符发一张全球唯一的身份证(码位)。项目取名 Unicode,就是“Unique, Universal, and Uniform character encoding”的缩写
码位 = 历史继承 + 国家编码整体搬迁 + 公开提案投票 + 区段规划形成的,不是随机;码位就是一个整数编号,每个字符都是固定死的,“二进制”只在“编码阶段”才出现,二进制字节序列是“编码规则”的产物,Unicode 本身 不规定任何字节长度,“中”之所以在 UTF-8 里用 3 个字节,是因为它被 UTF-8 这套算法 编码成了 24 位,而 不是 Unicode 表给它预留了 24 位。
str
(也就是 JSON 字符串)是 一串 Unicode 码位(逻辑字符)。.encode()
,所以它 还没变成 任何具体的字节序列(UTF-8、UTF-16、GBK……)。Unicode 字符(实际码位) → 编码 → 字节序列
字节序列 → 解码 → Unicode 字符(实际码位)
s = "中" # 内存里:Unicode 码位 U+4E2D b = s.encode('utf-8') # 编码 → 字节序列 b'\xe4\xb8\xad' s2 = b.decode('utf-8') # 解码 → 又得到 Unicode "中"
一句话
Unicode 字符本身只是一串“逻辑编号”;
编码/解码时,你告诉程序“按哪套规则把编号变成字节/把字节变回编号”,
这套规则就是 UTF-8、UTF-16、GBK ……
维度 | 举例 | 说给谁听 |
---|---|---|
字符集 | Unicode / UCS | 给“字符”发身份证 |
编码方案 | UTF-8 / UTF-16 / UTF-32 / GBK | 把身份证变成字节 |
运行时实现 | PEP 393(Latin-1/UCS-2/UCS-4 切换) | Python 解释器 内部 为了省内存 |
str
类型,str
(Unicode 字符串)序列化:
“序列化”最终就是把对象在内存中的 状态(通常是__dict__
里的键值对)
映射成纯文本(JSON 字符串)。
__dict__
)__dict__
里可能混有 datetime、Decimal、嵌套对象 —— 需要 钩子函数 或 类型判断 把它们“洗”成可 JSON 化的东西。__dict__
洗干净后变成 JSON,但“洗”这一步才是工作量所在。json
模块:跟我们自己手写一样,完全不保存方法、闭包、作用域——它只认“可 JSON 化的数据”。pickle
/ marshal
:这才是 Python 真正的“二进制序列化”库,它们会保存字节码、闭包、模块引用,但不再是文本,且跨版本/跨解释器有风险。json(文本序列化) 官方文档明确: “JSON can represent subsets of Python built-in types: dict, list, str, int, float, bool, None.” 任何 function、method、lambda、code object、frame、generator、file、socket 都会直接 raise TypeError。 对自定义类,只认 __dict__(或你提供的 default 钩子),不保存方法、闭包、局部变量。import json
def foo(): pass
json.dumps(foo) # TypeError: Object of type function is not JSON serializable
pickle
(二进制序列化)json
,就得接受“只存数据,不存代码”。pickle
,但要接受二进制和兼容性限制。
手写 JSON 序列化器。 import json from datetime import datetime from decimal import Decimal def default(obj, _seen=None): if _seen is None: _seen = set() oid = id(obj) if oid in _seen: # 防循环 return f"<CircularRef:{type(obj).__name__}>" _seen.add(oid) # 1) 基本可 JSON 类型 if isinstance(obj, (str, int, float, bool)) or obj is None: return obj # 2) 日期 / Decimal 等常用扩展 if isinstance(obj, datetime): return obj.isoformat() if isinstance(obj, Decimal): return float(obj) # 3) list / tuple → 递归列表 if isinstance(obj, (list, tuple)): return [default(x, _seen) for x in obj] # 4) dict → 递归键值 if isinstance(obj, dict): return {default(k, _seen): default(v, _seen) for k, v in obj.items()} # 5) 其它任何对象 → 反射取公开字段 # 过滤掉私有属性(以 _ 开头)和方法 fields = { k: default(v, _seen) for k, v in vars(obj).items() if not (k.startswith("_") or callable(v)) } # 顺便把类名放进去,方便调试 fields["__class__"] = type(obj).__name__ return fields # ------------------ 测试 ------------------ class Address: def __init__(self, city, street): self.city, self.street = city, street class User: def __init__(self, id_, name, addr, score): self.id = id_ self.name = name self.addr = addr self.score = Decimal(score) self.created = datetime.now() addr = Address("北京", "长安街") user = User(1, "张三", addr, "99.5") print(json.dumps(user, default=default, ensure_ascii=False, indent=2)) 核心思路 反射拿到实例的所有 公开字段(vars(obj) 或 __dict__)。 递归时: 遇到 基本类型 → 直接返回 遇到 list / dict / tuple → 继续递归 遇到 自定义类实例 → 再次反射它的 __dict__ 防循环引用 用 id(obj) 集合。 遇到无法 JSON 化的类型(datetime、Decimal…)→ 统一钩子处理。