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

如何避免构造Pydantic对象时复制实例属性?

阻止Pydantic复制属性实例的方法

问题描述

尝试构造一系列Pydantic对象,希望它们的某一属性都指向同一个已实例化的特定值,但Pydantic构造函数总会为每个对象分配该实例的副本。曾尝试在AttributeClass中添加Config类,测试copy_on_model_validation = False等选项,但均无效。

环境:Python 3.11、Pydantic 1.10(2.4版本也存在相同问题)

示例代码:

from abc import ABC
from pydantic import BaseModel

class Solver(BaseModel):
    pass
class MyClass(Solver):
    chain: tuple | None = None

class AttributeClass(Solver):
    myvar: float | None = None

class System(ABC):
    solver = None
    
one_var = 1
chain =[AttributeClass(myvar=one_var) for _ in range(1,5)]    

class MySystem(System):
    solver = MyClass(chain=chain)

if __name__ == "__main__":
    my_instance = MySystem()

    print("original ID: ", id(one_var))

    for i,s in enumerate(my_instance.solver.chain):
        print(f"ID n° {i+1}: {id(s.myvar)}")

问题根源

你的代码中存在类型不匹配:one_var是int类型,但AttributeClass的myvar声明为float | None。Pydantic在验证时会自动将int转换为float,生成新的float实例(比如1.0),导致每个myvar的ID与原one_var不同,看起来像是被复制了。

另外,copy_on_model_validation=False对不可变类型(如int、float)的效果有限,因为这类类型本身的赋值行为在Python中就是值传递,且小整数会被缓存,但类型转换会打破这个逻辑。

解决方案

1. 修正类型匹配

将AttributeClass的myvar类型改为包含int,让Pydantic无需进行类型转换:

class AttributeClass(Solver):
    myvar: int | float | None = None

此时one_var的int类型会直接被保留,由于Python对小整数的缓存机制,所有myvar都会指向同一个实例,ID与原one_var一致。

2. 使用自定义字段跳过类型转换(针对必须保持原类型引用的场景)

如果需要严格保持原实例的引用,即使类型不完全匹配,可以使用Pydantic的自定义验证逻辑跳过自动类型转换:

Pydantic 2.x版本:

from pydantic import BaseModel, Field, PlainSerializer

class AttributeClass(Solver):
    myvar: float | None = Field(
        default=None,
        serializer=PlainSerializer(lambda x: x, return_type=float if isinstance(x, (int, float)) else None)
    )

Pydantic 1.x版本:

from pydantic import BaseModel, validator

class AttributeClass(Solver):
    myvar: float | None = None

    @validator('myvar', pre=True)
    def keep_original_instance(cls, v):
        # 直接返回原实例,不进行类型转换
        return v

3. 使用可变容器封装值

如果需要保持对同一个值的引用(比如后续可能修改值并同步到所有对象),可以将不可变值封装到可变容器(如自定义类)中:

class ValueContainer:
    def __init__(self, value):
        self.value = value

one_var = ValueContainer(1)

class AttributeClass(Solver):
    myvar: ValueContainer | None = None

此时所有AttributeClass实例的myvar都会指向同一个ValueContainer实例,修改one_var.value会同步到所有对象的myvar.value。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 00:53:11