- иметь собственный инициализатор в подподклассе pydantic .BaseModel,
- которая показывает полную и завершенную подпись в Pylance,
- без копирования всех вручную имена полей в каждом конструкторе подкласса?
Предположим, у меня есть такая настройка:
from pydantic import BaseModel
class NumberWithMetadata(BaseModel):
value: float | int
source: str
class FloatWM(NumberWithMetadata):
value: float
Пока хорошо. В VS Code, когда я набираю FloatWM(, Pylance через IntelliSense показывает мне полную и правильную подпись, ограниченную только плавающими числами:
(*, value: float, source: str) -> FloatWM
Однако я хочу, чтобы мой подкласс выполнял пользовательскую логику во время инициализации, поэтому мне нужен инициализатор. Я не хочу, чтобы дочерний класс знал подробности полей базового класса, поэтому я **kwargs:
class FloatWM(NumberWithMetadata):
value: float
def __init__(self, value: float, **kwargs):
super().__init__(value = value * 1.618, **kwargs)
Во время выполнения это работает по желанию… но теперь, когда я набираю FloatWM( Pylance показывает только:
(*, value: float, **kwargs: Unknown) -> FloatWM
Есть ли синтаксис или шаблон, который я могу использовать, чтобы получить чистый IntelliSense в VS Code для подкласса?
Реальная установка< /h3>
Чтобы избежать проблемы XY с моим упрощенным кодом, приведенным выше:
Основной вариант использования — иметь много разных полей во многих разных классах, некоторые из которых оберните пинту.Количество. В каждом поле указывается требуемая размерность и базовая единица измерения для этого поля. Размерность применяется при создании значения для поля, а единицы измерения по умолчанию применяются, если они не указаны.
class ValueWithAttribution(BaseModel):
value: Any
source: str
# more fields here
class NumberWithAttribution(ValueWithAttribution):
value: float | int
type QuantityOrLiteral = float | int | str | PydanticPintQuantity
class QuantityWithAttribution(ValueWithAttribution):
value: Quantity
def __init__(self, value: QuantityOrLiteral, **kwargs):
units = self.__class__.__annotations__.get("value").__metadata__[0].units
if isinstance(value, (float, int)):
super().__init__(value=Quantity(value, units), **kwargs)
else:
if isinstance(value, str):
value = Quantity(value)
if value.units.is_compatible_with(units):
super().__init__(value=value, **kwargs)
else:
# raise hell
class Length(QuantityWithAttribution):
value: Annotated[Quantity, PydanticPintQuantity("meters")]
class Weight(QuantityWithAttribution):
value: Annotated[Quantity, PydanticPintQuantity("kg")]
class Person(MyBase):
height: Length
weight: Weight
me = Person(
height = Length('70 inches', source='I said so'),
weight = Weight(84, source='my treacherous scale')
)
Выходит за рамки этого вопроса, но связанная тема: хочу ли я, чтобы Pylance принимал позиционный аргумент при построении длины() и веса (), я считаю, что мне также придется добавить это к каждому такому конечному классу:
class Length(QuantityWithAttribution):
value: Annotated[Quantity, PydanticPintQuantity("meters")]
# This is not functionally needed, by is required for
# Pylance to accept that there's a positional argument :'(
def __init__(self, value: QuantityOrLiteral, **kwargs):
super().__init__(value=value, **kwargs)
Подробнее здесь: https://stackoverflow.com/questions/792 ... -basemodel