
使用 dill 序列化含自定义类实例的列表后,加载得到的对象无法通过 isinstance(obj, myclass) 检查,原因是 dill 默认重新构造类对象而非引用原始类;启用 byref=true 或改用 pickle 可修复该问题。
使用 dill 序列化含自定义类实例的列表后,加载得到的对象无法通过 isinstance(obj, myclass) 检查,原因是 dill 默认重新构造类对象而非引用原始类;启用 byref=true 或改用 pickle 可修复该问题。
在 Python 中,isinstance() 的行为依赖于对象的 __class__ 是否与目标类是同一对象(identity),而不仅仅是结构相同。dill 作为 pickle 的增强版,默认会对类定义本身进行深度序列化(即“内联”保存类的源码、闭包、装饰器等),并在反序列化时动态重建一个新类对象——尽管它与原始 MyClass 功能一致,但内存地址不同,因此 isinstance(y[0], MyClass) 返回 False。
可通过以下代码验证这一机制:
from pathlib import Path
import dill
class MyClass:
def __init__(self) -> None:
pass
path = Path('test/test.pkl')
path.parent.mkdir(exist_ok=True)
x = [MyClass()]
dill.dump(x, path.open('wb'))
y = dill.load(path.open('rb'))
print(isinstance(x[0], MyClass)) # True
print(isinstance(y[0], MyClass)) # False
# 查看类对象身份差异
print(f"Original MyClass id: {id(MyClass)}")
print(f"x[0].__class__ id: {id(x[0].__class__)}") # same
print(f"y[0].__class__ id: {id(y[0].__class__)}") # different!✅ 解决方案一:启用 byref=True(推荐)
设置 dill.settings['byref'] = True 后,dill 将像标准 pickle 一样,按模块路径引用类(如 '__main__.MyClass'),反序列化时直接导入并复用原始类对象,从而保证 isinstance() 正常工作:
dill.settings['byref'] = True # 关键配置:启用引用模式
x = [MyClass()]
dill.dump(x, path.open('wb'))
y = dill.load(path.open('rb'))
print(isinstance(x[0], MyClass)) # True
print(isinstance(y[0], MyClass)) # True ✅⚠️ 注意:byref=True 在跨进程/跨解释器场景下要求类定义在可导入的模块中(不建议在 __main__ 中定义用于持久化的类)。若类定义于脚本顶层,应将其移至独立 .py 文件(如 my_module.py)并显式导入使用。
✅ 解决方案二:改用标准 pickle
若无需 dill 对闭包、lambda、嵌套函数等高级对象的支持,pickle 是更轻量且语义更符合直觉的选择:
import pickle
x = [MyClass()]
pickle.dump(x, path.open('wb'))
y = pickle.load(path.open('rb'))
print(isinstance(x[0], MyClass)) # True
print(isinstance(y[0], MyClass)) # True ✅总结
- dill 的默认行为(byref=False)旨在支持更复杂的可序列化场景,但会牺牲类对象的身份一致性;
- isinstance() 失败不是 bug,而是其设计权衡的结果;
- 生产环境中涉及类型检查的序列化,优先启用 dill.settings['byref'] = True 或切换至 pickle;
- 始终确保被序列化的类定义稳定、可导入,避免因模块路径变更导致反序列化失败。

















