#python
#python #pydantic
Вопрос:
Я хочу сопоставить кортеж (список) с pydantic моделью.
Существует ли наилучшая практика для сопоставления индексов кортежей с атрибутами в следующих случаях?
from pydantic import BaseModel
class Ohlc(BaseModel):
close_time: float
open_time: float
high_price: float
low_price: float
close_price: float
volume: float
quote_volume: float
data = [
1495324800,
232660,
242460,
231962,
242460,
231.863,
0
]
Ответ №1:
Предполагая, что data
длина всегда равна количеству полей в вашей модели, вы можете использовать __fields__
для достижения этой цели.
Ohlc(**{key: data[i] for i, key in enumerate(Ohlc.__fields__.keys())})
(Раньше был fields
, который требовал, чтобы вы использовали construct()
сначала, но теперь он устарел, и теперь они говорят вам использовать __fields__
вместо этого).
Ответ №2:
Я столкнулся с проблемой simular и понял, что ее можно решить с помощью именованных кортежей и pydantic. Измененное решение ниже
from pydantic import BaseModel
import typing as t
data = [
1495324800,
232660,
242460,
231962,
242460,
231.863,
0
]
class OhlcEntry(t.NamedTuple):
close_time: float
open_time: float
high_price: float
low_price: float
close_price: float
volume: float
quote_volume: float
class Ohlc(BaseModel):
data: OhlcEntry
Ohlc(data=data)
Если вы работаете со списками смешанных типов, то тот же подход может быть расширен для анализа данных в списке:
from pydantic import BaseModel
import datetime as dt
import typing as t
js_data= """[
[43.88,-32.24,"2021-12-20T00:01:59Z"],
[43.95,-32.21,"2021-12-20T00:14:46Z"]
]"""
class Point(t.NamedTuple):
longitude: float
latitude: float
ts: dt.datetime
class Track(BaseModel):
__root__: t.List[Point]
Track.parse_raw(js_data)
Ответ №3:
В моем случае я реализовал ListModel
класс Point(x,y)
следующим образом.
Я делюсь примером, который не обрабатывал tuple
отдельно, предполагая, что вводится только list
ввод.
Я надеюсь, что это будет полезно.
from typing import List, Optional, Any, Dict, Union
from pydantic import BaseModel, PrivateAttr, root_validator, validator
class ListModel(BaseModel):
_list: List = PrivateAttr()
def __init__(self, obj: Optional[Union[List, BaseModel]] = None, **kwargs):
super().__init__(**self.parse_args(obj, kwargs))
# set private attribute
self._list = obj or list(kwargs.values())
def __iter__(self):
return iter(self._list)
def __getitem__(self, item):
return self._list[item]
def parse_args(self, obj: Optional[Union[List, BaseModel]], kwargs) -> Dict:
self.validate_init(obj, kwargs)
if isinstance(obj, list):
self.validate_list(obj)
return self.parse_list(obj)
elif isinstance(obj, BaseModel):
return obj.dict()
elif isinstance(obj, dict):
return obj
else:
return kwargs
def validate_init(self, obj: Optional[Union[List, BaseModel]], kwargs: Dict) -> None:
if all([any(obj), any(kwargs)]):
raise TypeError(
f"Allow input type exclusively either list or dict."
)
def validate_list(self, list_: List) -> bool:
# Check that the number of elements in the list (or tuple)
# matches the number of fields (attributes) in the Base Model
if len(list_) != len(self.__class__.__fields__.keys()):
raise ValueError(
f"List does not match the field of the {self.__class__.__name__}"
)
def parse_list(self, list_: List) -> Dict:
# Assign each element to each field
return {
field.name: field.type_(value)
for field, value in zip(self.__class__.__fields__.values(), list_)
}
class Point(ListModel):
x: int
y: int
Point([1, 2])
# x=1 y=2
class Box(ListModel):
left_top: Point
right_top: Point
right_bottom: Point
left_bottom: Point
Box([[1, 2], [1, 6], [4, 6], [4, 2]])
"""
left_top=Point(x=1, y=2) right_top=Point(x=1, y=6) right_bottom=Point(x=4, y=6) left_bottom=Point(x=4, y=2)
"""
box.left_top
# x=1 y=2
for point in box:
print(point)
"""
[1, 2]
[1, 6]
[4, 6]
[4, 2]
"""