直接用 json.dumps(obj.__dict__) 会出错,因为 dict 只含实例属性且不处理 datetime、set 等不可序列化类型;安全做法是先清洗 __dict__(过滤私有属性、转换非 json 类型),再序列化。

为什么直接用 json.dumps(obj.__dict__) 会出错
因为 __dict__ 只包含实例属性,不处理方法、不可序列化对象(如 datetime、set)、私有属性(以 _ 或 __ 开头)和继承来的属性。更常见的是遇到 TypeError: Object of type datetime is not JSON serializable —— 这不是 __dict__ 的问题,而是 json.dumps() 默认编码器不支持它。
如何安全地把 __dict__ 转成 JSON 字符串
核心是先清理/转换 __dict__ 中的值,再交给 json.dumps()。不要依赖默认行为:
- 过滤掉以
_开头的私有属性(比如_cache、__weakref__) - 对常见非 JSON 类型做预处理:把
datetime转成字符串,bytes解码,set转成list - 避免递归调用导致无限循环(比如对象之间互相引用)
一个轻量级做法是写个简单转换函数:
def to_dict(obj):
d = {}
for k, v in obj.__dict__.items():
if k.startswith('_'): # 跳过私有属性
continue
if isinstance(v, (datetime.datetime, datetime.date)):
d[k] = v.isoformat()
elif isinstance(v, bytes):
d[k] = v.decode('utf-8', errors='ignore')
elif isinstance(v, set):
d[k] = list(v)
elif hasattr(v, '__dict__') and not isinstance(v, (str, int, float, bool, type(None))):
d[k] = to_dict(v) # 仅对纯数据类递归,不碰内置类型
else:
d[k] = v
return d
<h1>用法</h1><p>import json
json_str = json.dumps(to_dict(my_instance), indent=2)</p>
dataclass + asdict() 是更稳的选择
如果你能控制类定义,@dataclass 配合 dataclasses.asdict() 比手动操作 __dict__ 更可靠,它自动跳过方法、处理嵌套 dataclass,并且不暴露私有字段(除非显式声明 init=False 或用 field(repr=False)):
- 必须确保所有字段都是可序列化的,否则仍会报错
-
asdict()不递归处理非 dataclass 对象(比如普通 class 实例),这点和手写to_dict不同 - 如果字段含
datetime,依然要自己后处理,或配合default参数传给json.dumps()
示例:
from dataclasses import dataclass, asdict
from datetime import datetime
<p>@dataclass
class User:
name: str
joined: datetime</p><p>u = User("Alice", datetime.now())</p><h1>asdict() 得到 dict,但 datetime 还在里头</h1><p>json.dumps(asdict(u), default=str) # 利用 default=str 处理 datetime</p>
别忽略循环引用和不可哈希对象
哪怕你清理了 __dict__,如果实例里存了自身引用(比如树节点的 parent 指针)或包含 dict / list 以外的不可序列化对象(如文件句柄、数据库连接、lambda),json.dumps() 依然会崩。这时候不能只靠 __dict__,得提前识别并剔除或替换这些字段。
一个实用技巧:用 vars(obj) 替代 obj.__dict__ —— 它更安全(不会触发自定义 __dict__ 属性),但行为基本一致;真正关键的是后续的数据清洗逻辑是否覆盖了你的实际字段类型。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











