Pydantic 从列表中设置变量

Sup*_*man 5 python class list pydantic

有没有办法从列表中设置 pydantic 模型?我尝试过这个,但它对我不起作用。如果 pydantic 无法做到这一点,如果我仍然需要类型验证和转换、约束等,那么最好的方法是什么?顺序在这里很重要。

from pydantic import BaseModel
from datetime import date


class User(BaseModel):
    id: int
    name = 'John Doe'
    sex: str
    money: float = None
    dt: date


data = [1, 'Tike Myson', 'male', None, '2022-01-20']
user = User(*data)

>>> TypeError: __init__() takes exactly 1 positional argument (6 given)
Run Code Online (Sandbox Code Playgroud)

mik*_*sus 3

我在这里部分回答了它:使用非关键字参数(又名 *args)初始化 FastAPI BaseModel,但我将在这里提供更多动态选项。

选项 1:使用属性的顺序

您的案例存在问题,Pydantic 不维护所有字段的顺序(至少取决于您是否设置类型)。如果您指定类型,name那么这有效:

from pydantic import BaseModel
from datetime import date


class User(BaseModel):
    id: int
    name: str = 'John Doe'
    sex: str
    money: float = None
    dt: date

    def __init__(self, *args):
        
        # Get a "list" of field names (or key view)
        field_names = self.__fields__.keys()
        
        # Combine the field names and args to a dict
        # using the positions.
        kwargs = dict(zip(field_names, args))
        
        super().__init__(**kwargs)
        

data = [1, 'Tike Myson', 'male', None, '2022-01-20']
user = User(*data)
Run Code Online (Sandbox Code Playgroud)

选项 2:将顺序设置为类变量

这样做的缺点是不那么动态,但不存在顺序不理想的问题。

from pydantic import BaseModel
from datetime import date


class User(BaseModel):
    id: int
    name = 'John Doe'
    sex: str
    money: float = None
    dt: date
        
    field_names: ClassVar = ('id', 'name', 'sex', 'money', 'dt')

    def __init__(self, *args):
                
        # Combine the field names and args to a dict
        # using the positions.
        kwargs = dict(zip(self.field_names, args))
        
        super().__init__(**kwargs)
        

data = [1, 'Tike Myson', 'male', None, '2022-01-20']
user = User(*data)
Run Code Online (Sandbox Code Playgroud)

选项 3:将它们硬编码到__init__

这与选项 2 类似,但有点简单(并且可重用性较差)。

from pydantic import BaseModel
from datetime import date
from typing import ClassVar


class User(BaseModel):
    id: int
    name = 'John Doe'
    sex: str
    money: float = None
    dt: date

    def __init__(self, id, name, sex, money, dt):
        super().__init__(id=id, name=name, sex=sex, money=money, dt=dt)
        

data = [1, 'Tike Myson', 'male', None, '2022-01-20']
user = User(*data)
Run Code Online (Sandbox Code Playgroud)

选项 4:不覆盖

另一种解决方案,但这不需要覆盖__init__. 然而,这在创建实例时需要更多代码:

from pydantic import BaseModel
from datetime import date


class User(BaseModel):
    id: int
    name = 'John Doe'
    sex: str
    money: float = None
    dt: date


data = [1, 'Tike Myson', 'male', None, '2022-01-20']

# Combine the field names and data
field_names = ['id', 'name', 'sex', 'money', 'dt']
kwargs = dict(zip(field_names, data))

# Create an instance of User from a dict
user = User(**kwargs)
Run Code Online (Sandbox Code Playgroud)

选项 1、2 和 3 的注释

如果您有多个地方需要它,请创建一个具有__init__重写的基类和子类。