Pydantic定义不可pickle的私有属性触发TypeError如何解决
问题根因
Pydantic v1 初始化私有属性时,会对类上定义的私有属性默认值自动执行深拷贝,而threading.Lock这类线程同步原语本身不支持深拷贝、pickle序列化,因此会抛出TypeError: cannot pickle '_thread.lock' object错误。
注意:arbitrary_types_allowed、copy_on_model_validation配置仅对公开模型字段生效,PrivateAttr(default=Lock())的写法依然会把Lock实例作为默认值传入,触发深拷贝逻辑,均无法解决该问题。不要通过设置underscore_attrs_are_private = False绕过问题,该配置会让下划线开头的属性被识别为公开模型字段,后续执行模型导出、校验、深拷贝操作时依然会触发Lock的序列化报错,属于治标不治本的写法。
解决方案
方案1:使用PrivateAttr的default_factory参数(推荐)
通过工厂函数延迟生成私有属性值,Pydantic会在每次实例化模型时调用工厂函数生成新的属性值,不会对提前定义的默认值做深拷贝,完美避开拷贝限制。默认情况下每个模型实例会持有独立的Lock实例,符合绝大多数场景下锁的使用需求:
from threading import Lock from pydantic import BaseModel, PrivateAttr class MyModel(BaseModel): class Config: underscore_attrs_are_private = True _lock: Lock = PrivateAttr(default_factory=Lock) # 运行验证 x = MyModel() x._lock.acquire() x._lock.release()
如果需要所有模型实例共享同一个全局锁,只需要把锁实例定义在类外部,工厂函数返回该实例的引用即可。
方案2:重写模型__init__方法赋值
如果不想显式引入PrivateAttr,可以在类上仅声明私有属性的类型,在__init__方法调用完父类初始化逻辑后,直接给实例的私有属性赋值,完全绕过Pydantic的私有属性默认值拷贝逻辑:
from threading import Lock from pydantic import BaseModel # 如需全局共享锁可定义在类外部 _SHARED_LOCK = Lock() class MyModel(BaseModel): class Config: underscore_attrs_are_private = True _lock: Lock def __init__(self, **data): super().__init__(**data) # 实例独立锁使用 self._lock = Lock() # 全局共享锁赋值如下 self._lock = _SHARED_LOCK x = MyModel()
内容的提问来源于stack exchange,提问作者miksus
相关产品推荐
相关产品推荐

