在 pydantic 模型中单独指定嵌套字典字段

Nor*_*ico 1 python pydantic

是否可以指定 pydantic 模型中包含的字典中的各个字段?我找不到任何东西,但也许我使用了错误的关键字。我在想这样的事情:

from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str = 'Jane Doe'
    stats = {
        age: int,
        height: float,
    }
Run Code Online (Sandbox Code Playgroud)

编辑:经过一些反馈后,我觉得我需要澄清一些条件并给出一个更完整的示例。我想要做的与此更相似:

from pydantic import BaseModel, NonNegativeInt, NonNegativeFloat
from pydantic.generics import GenericModel

DataT = TypeVar('DataT')

class Trait(GenericModel, Generic[DataT]):
    descript: str
    value: DataT

class CharacterBarbarian(BaseModel):
    id: int
    name: str = 'Barbarok'
    traits = {
        strength: Trait[NonNegativeInt] = Trait[NonNegativeInt](descript='the force', value=18),
        height: Trait[NonNegativeFloat] = Trait[NonNegativeFloat](descript='tallness', value=1.8),
        weight: Trait[NonNegativeFloat] = Trait[NonNegativeFloat](descript='width', value=92.1),
    }

class CharacterWizard(BaseModel):
    id: int
    name: str = 'Supremus'
    traits = {
        intelligence: Trait[NonNegativeInt] = Trait[NonNegativeInt](descript='smarts', value=16),
        spells: Trait[NonNegativeInt] = Trait[NonNegativeInt](descript='number of them', value=4),
    }


SavedWizard_dict = { # Read from file for example
    'id': 1234,
    'name': "Gandalf",
    'traits': {
        'intelligence': {'descript': 'smarts', 'value': 20}
        'spells': {'descript': 'number of them', 'value': 100),
    },
}
SavedWizard = CharacterWizard(**SavedWizard_dict)
Run Code Online (Sandbox Code Playgroud)

所以基本上我试图利用 pydantic 的内在能力来序列化/反序列化 dict/json 来保存和初始化我的类。同时,这些 pydantic 类由通用 pydantic 类的特定版本的列表/字典组成,但这些 pydantic 类的选择因类而异。

Mat*_*oni 5

您可以使用嵌套类执行类似的操作:

from pydantic import BaseModel

class UserStats(BaseModel):
    age: int
    height: float

class User(BaseModel):
    id: int
    name = 'Jane Doe'
    stats: UserStats
Run Code Online (Sandbox Code Playgroud)

然后,当您构造任何User实例时,您可以将 stats 字段作为字典传递,它将自动转换:

user = User(id=1234, stats={"age": 30, "height": 180.0})
Run Code Online (Sandbox Code Playgroud)

唯一的区别是 的stats字段User是一个类( 的实例UserStats),因此如果您想访问它的字段,您需要使用属性访问而不是使用字典访问:

print(user.age)  # ok!
print(user["age"])  # not ok...
Run Code Online (Sandbox Code Playgroud)

如果您需要 ste stats 属性作为字典,那么您可以TypedDicttyping_extensions(python3.7) 或typing(python3.8+) 模块使用:

from typing import TypedDict
from pydantic import BaseModel

class UserStats(TypedDict):
    age: int
    height: float

class User(BaseModel):
    id: int
    name = 'Jane Doe'
    stats: UserStats

user = User(id=1234, stats={"age": 30, "height": 180.0})
print(user.stats["age"])  # will work!
Run Code Online (Sandbox Code Playgroud)

编辑:

正如这个答案的评论中提到的,问题作者不想全局定义该类UserStats以避免污染。

这可以通过直接在类内部定义类来解决User

class User(BaseModel):
     id: int
     name = "Jane Doe"
     class Stats(TypedDict):
         age: int
         height: float
     stats: Stats
Run Code Online (Sandbox Code Playgroud)

这允许多个类在全局命名空间中User分别定义它们的类stats,而无需重复的类。Stats

更巧合但对类型检查器和语言服务器不太友好的方法是使用以下函数式 API TypedDict

 class User(BaseModel):
     id: int
     name = "Jane Doe"
     stats: TypedDict("Stats", age=int, height=float)
Run Code Online (Sandbox Code Playgroud)