是否可以动态更改 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_mutation
是 True
),然后一次设置一个属性(直接或通过方法),然后在最后,调用一些方法使其不可变(allow_mutation
是 False
):
示例代码:
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
关于此的一些说明:
访问 private/internal __config__
对象可能不是一个面向未来的解决方案。如果它被重命名或者 Config
对象的使用方式发生变化,那么这个解决方案就会中断。文档的 ModelConfig 部分也没有提到它,因此它可能不打算像这样使用。
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')
我正在考虑使用 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_mutation
是 True
),然后一次设置一个属性(直接或通过方法),然后在最后,调用一些方法使其不可变(allow_mutation
是 False
):
示例代码:
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
关于此的一些说明:
访问 private/internal
__config__
对象可能不是一个面向未来的解决方案。如果它被重命名或者Config
对象的使用方式发生变化,那么这个解决方案就会中断。文档的 ModelConfig 部分也没有提到它,因此它可能不打算像这样使用。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')