如何从位置而不是名称给出的字段值初始化 Pydantic 对象?

问题描述 投票:0回答:5

我无法找到一种简单的方法来根据位置给定的字段值(例如在列表而不是字典中)初始化 Pydantic 对象,因此我编写了类方法

positional_fields()
来从可迭代创建所需的字典:

from typing import Optional, Iterable, Any, Dict
from pydantic import BaseModel


class StaticRoute(BaseModel):
    if_name: str
    dest_ip: str
    mask: str
    gateway_ip: str
    distance: Optional[int]
    
    @classmethod
    def positional_fields(cls, values: Iterable) -> Dict[str, Any]:
        return dict(zip(cls.__fields__, values))


input_lines = """
  route ab 10.0.0.0 255.0.0.0 10.220.196.23 1
  route gh 10.0.2.61 255.255.255.255 10.220.198.38 1
""".splitlines()

for line in input_lines:
    words = line.split()
    if words and words[0] == 'route':
        sroute = StaticRoute(**StaticRoute.positional_fields(words[1:]))
        print(sroute)
if_name='ab' dest_ip='10.0.0.0' mask='255.0.0.0' gateway_ip='10.220.196.23' distance=1
if_name='gh' dest_ip='10.0.2.61' mask='255.255.255.255' gateway_ip='10.220.198.38' distance=1

有没有更直接的方法来实现这一目标?

我的方法期望

__fields__
字典的键按照类中定义字段的顺序排列。我不确定这是否得到保证(假设 Python 3.6+)。

python iterable pydantic
5个回答
1
投票

使用 dataclasses 怎么样?比如:

from typing import Optional

from pydantic.dataclasses import dataclass


@dataclass
class StaticRoute:
    if_name: str
    dest_ip: str
    mask: str
    gateway_ip: str
    distance: Optional[int]


words = "route if_name dest_ip mask gateway_ip 10".split()
print(StaticRoute(*words[1:])

# StaticRoute(if_name='if_name', dest_ip='dest_ip', mask='mask', gateway_ip='gateway_ip', distance=10)

1
投票

您可以使用 custom RootTypeNamedTuple 的组合,如下所示:

from pydantic import BaseModel
from typing import NamedTuple, Optional


class StaticRouteTuple(NamedTuple):
    if_name: str
    dest_ip: str
    mask: str
    gateway_ip: str
    distance: Optional[int]


class StaticRoute(BaseModel):
    __root__: StaticRouteTuple

    @property
    def route(self) -> StaticRouteTuple:
        return self.__root__


input_lines = """
  route ab 10.0.0.0 255.0.0.0 10.220.196.23 1
  route gh 10.0.2.61 255.255.255.255 10.220.198.38 1
""".splitlines()

for line in input_lines:
    words = line.split()
    if words and words[0] == "route":
        sroute = StaticRoute.parse_obj(words[1:]).route
        print(sroute)

如果您不想使用自定义 RootType,您可以使用

pyandic.parse_obj_as
,例如:

from pydantic import parse_obj_as
sroute = parse_obj_as(StaticRouteTuple, words[1:])

0
投票

类方法

BaseModel.parse_obj()
返回一个由字典初始化的对象实例。我们可以创建一个类似的类方法
parse_iterable()
,它接受一个可迭代对象。

from typing import Optional, Iterable, Any, Dict
from pydantic import BaseModel


class BaseModelExt(BaseModel):
    @classmethod
    def parse_iterable(cls, values: Iterable):
        return cls.parse_obj(dict(zip(cls.__fields__, values)))

    
class StaticRoute(BaseModelExt):
    if_name: str
    dest_ip: str
    mask: str
    gateway_ip: str
    distance: Optional[int]


input_lines = """
  route ab 10.0.0.0 255.0.0.0 10.220.196.23 1
  route gh 10.0.2.61 255.255.255.255 10.220.198.38 1
""".splitlines()

for line in input_lines:
    words = line.split()
    if words and words[0] == 'route':
        sroute = StaticRoute.parse_iterable(words[1:])
        print(sroute)

注意:如果

BaseModel.__fields__
的订单得到保证,我们仍然缺少确认。


0
投票

当前 Pydantic

Field
支持
kw_only
属性
,这将允许您使用位置字段创建模型:

from pydantic import Field
from pydantic.dataclasses import dataclass


@dataclass
class MyModel:
    a: str = Field(kw_only=False)
    b: str = Field(kw_only=False)


model_arg = MyModel("test", "model")
model_kw = MyModel("test", b="model")
model_kw2 = MyModel(a="test", b="model")

print(model_arg, model_kw, model_kw2)

0
投票

这个解决方案完全是 Pydantic 的。

输入是通过串行连接返回的一些字符串,我按顺序将其解析到模型中。

虽然这可行,但我不知道这种风险是否值得节省代码。如果未来的开发人员打乱了模型中字段的顺序,则将很难调试该问题。

from pydantic import BaseModel


class IrisResponse(BaseModel):
    code: str
    char_t: str
    value: str
    char_s: str
    num1: str
    num2: str
    num3: str
    num4: str

    def __init__(self, value: str):
        super().__init__(**dict(zip(IrisResponse.__fields__, value.split())))


out = "03: T        90000 S= 0001 0001 0000 1000 "
result = IrisResponse(out)
print(result.__repr__())

输出:

IrisResponse(code='03:', char_t='T', value='90000', char_s='S=', num1='0001', num2='0001', num3='0000', num4='1000')
© www.soinside.com 2019 - 2024. All rights reserved.