You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pydantic开启validate_assignment时如何实现引用赋值而非副本?

Pydantic 2.0 赋值时保留引用的实现方案

问题场景与示例代码

# 可行但无赋值验证:
from pydantic import BaseModel

class One(BaseModel):
    field1: list

class Two(One):
    field2: list
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.field1 = self.field2  # 能保留引用,但不会触发赋值验证 :(

# 带赋值验证,但存在引用/副本问题
class OneV2(BaseModel):
    field1: list
    class Config:
        validate_assignment = True
       
class TwoV2(OneV2):
    field2: list
    class Config:
        validate_assignment = True
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.field1 = self.field2  # 结果是深拷贝,而非引用赋值

# 测试
_2 = Two(field1=[1], field2=[1, 2, 3])
print(_2.field1 is _2.field2)  # True,符合预期

_2_v2 = TwoV2(field1=[1, 2, 3], field2=[1, 2, 3, 4])
print(_2_v2.field1 is _2_v2.field2)  # False,不符合预期

需求明确:在开启validate_assignment的TwoV2类中,将field2的引用直接赋值给field1,同时保留赋值验证功能。

解决方案

方法1:直接操作实例的__dict__

绕过Pydantic的赋值拦截逻辑,直接修改实例内部的属性存储:

from pydantic import BaseModel

class OneV2(BaseModel):
    field1: list
    class Config:
        validate_assignment = True
       
class TwoV2(OneV2):
    field2: list
    class Config:
        validate_assignment = True
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # 直接修改__dict__,保留引用且不影响其他属性的验证
        self.__dict__['field1'] = self.field2

# 测试验证
_2_v2 = TwoV2(field1=[1,2,3], field2=[4,5,6])
print(_2_v2.field1 is _2_v2.field2)  # True,引用保留
# 验证仍生效:非法赋值会触发报错
try:
    _2_v2.field1 = "invalid type"
except ValueError as e:
    print(e)  # 输出验证错误信息

方法2:使用model_setattr并关闭单次验证

使用Pydantic官方提供的model_setattr方法,通过validate=False参数跳过当前赋值的验证,从而保留引用:

from pydantic import BaseModel

class OneV2(BaseModel):
    field1: list
    class Config:
        validate_assignment = True
       
class TwoV2(OneV2):
    field2: list
    class Config:
        validate_assignment = True
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # 调用model_setattr,关闭本次赋值的验证
        self.model_setattr('field1', self.field2, validate=False)

# 测试验证
_2_v2 = TwoV2(field1=[1,2,3], field2=[4,5,6])
print(_2_v2.field1 is _2_v2.field2)  # True,引用保留
# 其他赋值仍会触发验证
try:
    _2_v2.field2 = 123
except ValueError as e:
    print(e)  # 输出验证错误信息

原理说明

当validate_assignment=True时,Pydantic会拦截所有属性赋值操作,对输入值进行验证、解析和深拷贝(针对列表、字典这类可变类型),目的是保证实例数据的一致性和不可变性。如果需要保留引用,必须绕过这个拦截逻辑:

  • 直接操作__dict__是最直接的方式,因为Pydantic实例的属性实际存储在__dict__中,跳过了验证拦截
  • model_setattr是官方推荐的属性设置方法,通过validate=False可以灵活跳过单次验证,比直接操作__dict__更符合Pydantic的设计规范

内容的提问来源于stack exchange,提问作者deponovo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 17:10:30