__reduce__ 返回值必须是二元组(可调用对象,参数元组)或三元组(可调用对象,参数元组,状态字典),否则抛 typeerror;支持绕过 __init__ 重建对象,优先于 __getstate__/__setstate__。

__reduce__ 返回什么才算合法?
必须返回一个可调用对象(比如函数或类)和它的参数元组,Python 会用它们重建对象。常见写法是 return (cls, (arg1, arg2)) 或 return (func, (arg,))。如果返回值不是二元组、第一个不是可调用、第二个不是可序列化的元组或字典,pickle.dump 会直接抛 TypeError: expected 2-item tuple。
- 返回三元组(如加状态字典)也合法:
return (cls, (a, b), state_dict),但第三个元素会被自动赋给__setstate__ - 返回单个可调用(无参数)也可以,但必须是
(callable, ()),不能只写callable - 避免在
__reduce__里引用未导出的闭包或局部函数,否则 unpickle 时会报AttributeError: Can't get attribute ...
如何跳过某些属性不序列化?
典型场景是类里有文件句柄、线程锁、临时缓存等不可序列化字段。不能靠删属性硬来,得在 __reduce__ 中显式控制构造参数,并配合 __setstate__ 补充非关键状态。
- 只传核心数据进构造函数,比如
return (MyClass, (self._id, self._name)),忽略self._cache和self._lock - 若需保留部分运行时状态,可在返回三元组时提供
__setstate__兼容的字典:return (MyClass, (self._id,), {'_name': self._name}) - 别在
__reduce__里手动调用delattr或清空属性——unpickle 是全新实例,原对象状态本就不该干扰重建过程
为什么 __reduce__ 比 __getstate__ / __setstate__ 更底层?
__reduce__ 完全接管重建逻辑,而 __getstate__ 只是定制 __dict__ 的子集;前者能绕过 __init__,后者必须走初始化流程。当你需要反序列化时用工厂函数、或从不同参数重建对象,就得用 __reduce__。
- 例如:类实例本由数据库 ID 构建,但 pickle 时只想存 ID,unpickle 时重新查库 —— 写
return (load_from_db, (self.db_id,)) - 再如:继承自不可修改的第三方类,无法重写
__init__,但又需定制序列化 ——__reduce__是唯一出口 - 注意:若同时定义了
__getstate__,它会被忽略,除非__reduce__显式调用它
调试 __reduce__ 失败的常见信号
报错信息往往不直观,重点盯住 traceback 顶端那行:
-
RecursionError: maximum recursion depth exceeded→__reduce__里不小心引用了self自身(比如打印self.__dict__),触发无限递归 -
AttributeError: 'module' object has no attribute 'xxx'→ 返回的可调用对象路径不对,比如函数在局部作用域定义,或模块没正确导入 - 反序列化后字段为
None或默认值 → 构造函数参数顺序错,或元组里漏传某个必需参数 - 用
pickle.HIGHEST_PROTOCOL时出问题,但低版本协议正常 → 某些高版本协议对 callable 的序列化更严格,优先用functools.partial替代 lambda
最稳的验证方式是:写完立刻跑一遍 obj2 = pickle.loads(pickle.dumps(obj)),别跳过这步。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











