如何为Pydantic BaseModel子类私有属性实现带验证的自定义__init__
Pydantic BaseModel私有属性在自定义__init__中的问题解决
问题描述
创建带有自定义__init__方法的Pydantic BaseModel子类时,尝试通过super().__init__(**kwargs)传递下划线前缀的私有属性(如_a、_b),执行时触发AttributeError: 'MyClass' object has no attribute '_a'异常,但将私有属性改为非下划线前缀后运行正常。需要保留现有结构同时使用私有属性。
示例代码
from pydantic import BaseModel, ValidationError, model_validator c2b = { "work week": 1, "weekend": 2, } class MyClass(BaseModel): _a: int _b: int @model_validator(mode="after") def validator(self) -> None: if self._a <= 0: raise ValidationError("a must be positive.") def __init__(self, a: int, c: str) -> None: super().__init__( _a=a, _b=c2b[c], ) MyClass(1, "weekend")
原因分析
Pydantic默认将以下划线开头的字段归为受保护命名空间(默认protected_namespaces包含('model_', '_')),不会把这些字段纳入模型的字段系统。因此调用super().__init__()时传入的_a、_b不会被处理并设置到实例上,导致验证器无法访问这些属性。
解决方案
方案1:使用PrivateAttr显式声明私有属性
通过PrivateAttr()标记私有属性,让Pydantic将其识别为实例私有属性,直接在自定义__init__中赋值:
from pydantic import BaseModel, ValidationError, model_validator, PrivateAttr c2b = { "work week": 1, "weekend": 2, } class MyClass(BaseModel): _a: int = PrivateAttr() _b: int = PrivateAttr() @model_validator(mode="after") def validator(self) -> None: if self._a <= 0: raise ValidationError("a must be positive.") def __init__(self, a: int, c: str) -> None: # 直接为私有属性赋值 self._a = a self._b = c2b[c] # 调用父类初始化,无需传递私有属性参数 super().__init__() MyClass(1, "weekend")
方案2:修改模型配置,允许下划线开头的字段作为普通模型字段
通过model_config将protected_namespaces设置为空元组,取消下划线开头字段的保护限制:
from pydantic import BaseModel, ValidationError, model_validator c2b = { "work week": 1, "weekend": 2, } class MyClass(BaseModel): _a: int _b: int # 取消下划线开头字段的保护限制 model_config = {"protected_namespaces": ()} @model_validator(mode="after") def validator(self) -> None: if self._a <= 0: raise ValidationError("a must be positive.") def __init__(self, a: int, c: str) -> None: super().__init__( _a=a, _b=c2b[c], ) MyClass(1, "weekend")
内容的提问来源于stack exchange,提问作者Maksym Petrenko
相关产品推荐
相关产品推荐

