会pickle失败的类对象包括:定义在函数或交互式环境中的类、绑定lambda/嵌套函数/局部functools.partial的实例、含threading.Lock/文件对象/数据库连接的实例、使用__slots__但未定义__getstate__且存在未声明动态属性的类。

不能直接用 pickle.dump() 保存所有类实例——尤其是含线程、文件句柄、Lambda、模块级闭包或 C 扩展对象的类。
哪些类对象会 pickle 失败?
常见报错是 AttributeError: Can't pickle local object 或 TypeError: cannot pickle '_thread.RLock' object。根本原因是 pickle 只能序列化「可重建」的状态:它不保存内存地址或运行时资源,只存模块路径 + 类名 + __dict__(或 __getstate__() 返回值)。
- 定义在函数/交互式环境里的类(无稳定模块路径)→ 失败
- 实例绑定了
lambda、嵌套函数或局部functools.partial→ 失败 - 含
threading.Lock、open()返回的文件对象、数据库连接 → 失败 - 使用了
__slots__但没定义__getstate__,且存在未声明的动态属性 → 可能丢失数据
手动控制 pickle 行为:用 __getstate__ 和 __setstate__
这是最常用也最可控的方式。让类显式决定哪些字段进 pickle,哪些跳过,并在反序列化时补全不可持久化的资源。
import threading
import pickle
<p>class Worker:
def <strong>init</strong>(self, name, data):
self.name = name
self.data = data
self.lock = threading.RLock() # 不可 pickle
self.cache = {} # 可选:运行时缓存,也不存</p><pre class="brush:php;toolbar:false;">def __getstate__(self):
# 返回要保存的字典;剔除不可序列化项
state = self.__dict__.copy()
del state['lock']
del state['cache']
return state
def __setstate__(self, state):
# 恢复基础状态
self.__dict__.update(state)
# 重建运行时资源
self.lock = threading.RLock()
self.cache = {}
注意:__setstate__ 必须能被模块顶层访问到(即类本身得在 sys.modules 中有明确路径),否则反序列化时找不到类定义。
用 copyreg.pickle() 注册自定义序列化函数
适合无法修改类源码的场景(比如第三方类)。需提供一个可调用对象,返回 (callable, args) 元组,用于重建对象。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
-
callable必须是模块级函数,不能是 lambda 或闭包 -
args必须全可 pickle(字符串、数字、普通 dict/list 等) - 注册后,所有该类型的实例都会走这个逻辑
import copyreg import threading <p>def pickle_worker(obj):</p><h1>只保留核心数据,丢弃 lock</h1><pre class="brush:php;toolbar:false;">return Worker, (obj.name, obj.data)
假设 Worker 类已定义且无法修改
copyreg.pickle(Worker, pickle_worker)
这种方式绕过了 __getstate__,但要求你完全掌控重建逻辑;若 Worker.__init__ 参数变了,这里就容易出错。
保存前务必检查对象图的可达性
一个看似简单的实例,可能隐式引用了不可 pickle 的全局对象(比如日志器、Flask app 实例、matplotlib.pyplot)。用 pickle.dumps(obj) 测试是最直接的办法:
try:
pickle.dumps(my_obj)
except Exception as e:
print(f"Pickling failed: {e}")
# 用 traceback 或手动 inspect obj.__dict__ 查哪一层出问题
特别容易忽略的是:类本身用了 __new__ 或元类,且返回了非标准实例;或者实例绑定了用 types.MethodType 动态添加的方法——这些都可能破坏 pickle 链。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










