I'm implementing a Python Interface using the abstract base class (known as the strategy pattern). I want to be able to do this with Pydantic.
Without Pydantic, I would use properties, like this:
from abc import ABC, abstractproperty
@dataclass
class PersonEntity(ABC):
@abstractproperty
def first_name(self):
raise NotImplementedError
@abstractproperty
def last_name(self):
raise NotImplementedError
@dataclass
class Person(PersonEntity):
@property
def first_name(self):
return 'Jimmy'
@property
def last_name(self):
return 'Kelly'
This way, if I were to implement another class, like
@dataclass
class SillyPerson(PersonEntity):
@property
def first_name(self):
return 'Jimmy'
@property
def last_name(self):
return 'Kelly'
@property
def sillyness(self):
return 5
then the interface will throw an error. This helps constrain any new class that inherits from PersonEntity
.
However, I want to spit this information into a FastAPI response object. I cannot do this without figuring out some kind of serializer to grab all the values of each property field, and just the property fields (which I'm struggling to do).
I would rather use Pydantic. In this case, I dont need properties, I can simply do:
from pydantic import BaseModel
class PersonEntity(ABC, BaseModel):
first_name: str
last_name: str
class Person(PersonEntity):
first_name: str
last_name: str
These will serialize in the way that I need, but I lose the interface functionality because now I have no properties, and therefore cannot use @abstractproperty
.
So if I were to implement
class SillyPerson(PersonEntity):
first_name: str
last_name: str
sillyness: str
there's no error, because pydantic allows this.
(Incidentally, I wasn't sure in these examples whether to inherit from BaseModel in child classes or not.)
Is there some way I can constrain the Pydantic model to give me the interface behaviour that I need, throwing errors when a field is introduced that is not included in the ABC PersonEntity
class?
In your pydantic BaseModel definition, you can configure it to forbid extra parameters.
from pydantic import BaseModel, Extra
class PersonEntity(BaseModel):
class Config:
extra = Extra.forbid
first_name: str
last_name: str
Then constructing an instance of this with extra args:
sillyperson = PersonEntity(first_name='foo', last_name='bar', sillyness='zar')
Throws an error:
pydantic.error_wrappers.ValidationError: 1 validation error for PersonEntity
sillyness
extra fields not permitted (type=value_error.extra)
What you currently have implemented will not work since
class SillyPerson(PersonEntity):
first_name: str
last_name: str
sillyness: str
defines a new dataclass (it inherits from BaseModel) with a field called 'sillyness'. It will therefore do the opposite of you want; throwing an error if an attribute called sillyness is not provided.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.