是否可以动态更改 pydantic class 的可变性?

Is it possible to dynamically change the mutability of a pydantic class?

我正在考虑使用 pydantic class 来支持构建器模式,其中设置对象值,一次一个属性,然后在最后冻结对象值。

为此,在 pydantic 中是否可以在开始可变后使对象不可变?

如果 class 是从 class 中的 BaseModel, then mutability/immutability is configured by adding a Model Config 子class 编辑的 class 并且 allow_mutation 属性设置为 True/ False.

示例代码:

from pydantic import BaseModel, NonNegativeInt

class Person(BaseModel):
    name: str
    age: NonNegativeInt

    class Config:
        allow_mutation = False

p = Person(name='Jack', age=15)
p.name = 'Jill'

示例输出:

TypeError: "Person" is immutable and does not support item assignment

现在,这个 Config 对象和 allow_mutation 属性(或任何其他模型配置属性)也可以通过 class 的方法访问和切换:

self.__config__.allow_mutation

示例代码:

class Person(BaseModel):
    name: str
    age: NonNegativeInt

    def __init__(self, **data) -> None:
        super().__init__(**data)
        self.__config__.allow_mutation = False

    def is_mutable(self):
        print(self.__config__, self.__config__.allow_mutation)

p = Person(name='Jack', age=15)
p.is_mutable()
p.name = 'Jill'

示例输出:

<class '__main__.Config'> False
Traceback (most recent call last):
  File "test.py", line 18, in <module>
    p.name = 'Jill'
  File "pydantic/main.py", line 418, in pydantic.main.BaseModel.__setattr__
TypeError: "Person" is immutable and does not support item assignment

将它们放在一起,您可以使 class 最初可变(allow_mutationTrue),然后一次设置一个属性(直接或通过方法),然后在最后,调用一些方法使其不可变(allow_mutationFalse):

示例代码:

from pydantic import BaseModel, NonNegativeInt

class Person(BaseModel):
    name: str
    age: NonNegativeInt

    def __init__(self, **data) -> None:
        super().__init__(**data)
        self.__config__.allow_mutation = True

    def build(self):
        self.__config__.allow_mutation = False

print('-- Create a Person --')
p = Person(name='Jack', age=15)
print(p)

print('-- It is initially mutable, can set its attributes --')
p.name = 'Jill'
p.age = 17
print(p)

print('-- Making it immutable --')
p.build()

p.name = 'Jack'

示例输出:

-- Create a Person --
name='Jack' age=15
-- It is initially mutable, can set its attributes --
name='Jill' age=17
-- Making it immutable --
Traceback (most recent call last):
  File "test.py", line 29, in <module>
    p.name = 'Jack'
  File "pydantic/main.py", line 418, in pydantic.main.BaseModel.__setattr__
TypeError: "Person" is immutable and does not support item assignment

关于此的一些说明:

  1. 访问 private/internal __config__ 对象可能不是一个面向未来的解决方案。如果它被重命名或者 Config 对象的使用方式发生变化,那么这个解决方案就会中断。文档的 ModelConfig 部分也没有提到它,因此它可能不打算像这样使用。

  2. Pydantic 提到这个 allow_mutation 只是“人造不变性”。如果 allow_mutation 为假,它基本上会中止 __setattr__ 调用:

    # In pydantic/main.py > BaseModel
    
        elif not self.__config__.allow_mutation or self.__config__.frozen:
            raise TypeError(f'"{self.__class__.__name__}" is immutable and does not support item assignment')