我无法找到一种简单的方法来根据位置给定的字段值(例如在列表而不是字典中)初始化 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+)。
使用 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)
您可以使用 custom RootType 和 NamedTuple 的组合,如下所示:
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)
pyandic.parse_obj_as
,例如:
from pydantic import parse_obj_as
sroute = parse_obj_as(StaticRouteTuple, words[1:])
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__
的订单得到保证,我们仍然缺少确认。
当前 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)
这个解决方案完全是 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')