Pydantic#
Private Attributes#
from typing import Optional
from pydantic import BaseModel, ConfigDict
from datetime import datetime
class TodoItem(BaseModel):
content: str
_is_completed: bool = False
_created_at: datetime = datetime.now()
_completed_at: Optional[datetime] = None
model_config = ConfigDict(
frozen=True,
)
@property
def is_completed(self):
return self._is_completed
def complete(self) -> None:
self._is_completed = True
self._completed_at = datetime.now()
def spent_time(self) -> Optional[datetime]:
if self._completed_at is None:
return None
else:
return self._completed_at - self._created_at
item = TodoItem(content="Do homework")
print(item.spent_time())
item.complete()
print(item.spent_time())
None
0:00:00.001064
item._is_completed = True
from typing import Optional
from pydantic import BaseModel, PrivateAttr
from datetime import datetime
class TodoItem(BaseModel):
content: str
_is_completed: bool = PrivateAttr(default=False)
_created_at: datetime = PrivateAttr(default_factory=datetime.now)
_completed_at: Optional[datetime] = PrivateAttr(default=None)
@property
def is_completed(self):
return self._is_completed
def complete(self) -> None:
self._is_completed = True
self._completed_at = datetime.now()
def spent_time(self) -> Optional[datetime]:
if self._completed_at is None:
return None
else:
return self._completed_at - self._created_at
item = TodoItem(content="Do homework")
print(item.spent_time())
item.complete()
print(item.spent_time())
None
0:00:00.000242
item._is_completed = False
item.model_dump()
{'content': 'Do homework'}
item._is_completed = False
item._is_completed
False
Class Attributes#
Class attributes can be defined with the type annotation ClassVar
.
from typing import ClassVar
from pydantic import BaseModel
class NameFormatter:
def __init__(self, capitalized: bool = False) -> None:
self._capitalized = capitalized
def format(self, name: str) -> str:
name = name.strip().title()
if self._capitalized:
name = name.upper()
return name
class User(BaseModel):
name_formatter: ClassVar[NameFormatter]
name: str
age: int
@property
def formatted_name(self) -> str:
return self.name_formatter.format(self.name)
user = User(name="isaac fei", age=23)
user
User(name='isaac fei', age=23)
User.name_formatter = NameFormatter(capitalized=True)
user.formatted_name
'ISAAC FEI'
Post Initialization#
Decorating with model_validator
#
from pydantic import BaseModel, model_validator
class User(BaseModel):
name: str
age: int
@model_validator(mode="after")
def init_lucky_number(self) -> None:
# Create a private attribute
# using initialized attributes
self._lucky_number = hash(self.name)
@property
def lucky_number(self) -> int:
return self._lucky_number
Note that we defined self._lucky_number
as a private attribute with an underscore prefix. If we use self.lucky_number, an exception will be triggered due to a violation of Pydanticâs validation rules.
To expose the value of self._lucky_number
, we can make it a property.
user = User(name="Isaac", age=23)
user.lucky_number
-662702122252289913
Overriding model_post_init
#
Overriding the BaseModel
âs method model_post_init
is the preferred way of conducting the post initialization of the instance.
from typing import Any
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
def model_post_init(self, __context: Any) -> None:
# Call method of super class
super().model_post_init(__context)
# Create a private attribute
# using initialized attributes
self._lucky_number = hash(self.name)
@property
def lucky_number(self) -> int:
return self._lucky_number
user = User(name="Isaac", age=23)
user.lucky_number
-662702122252289913