Сериализация
Помимо прямого доступа к атрибутам модели через имена их полей (например, model.foobar
), модели можно конвертировать, выгружать, сериализовать и экспортировать несколькими способами.
!!! Совет «Сериализация и дамп» Pydantic использует термины «сериализация» и «дамп» как синонимы. Оба относятся к процессу преобразования модели в словарь или строку в формате JSON.
Outside of Pydantic, the word "serialize" usually refers to converting in-memory data into a string or bytes.
However, in the context of Pydantic, there is a very close relationship between converting an object from a more
structured form — such as a Pydantic model, a dataclass, etc. — into a less structured form comprised of
Python built-ins such as dict.
While we could (and on occasion, do) distinguish between these scenarios by using the word "dump" when converting to
primitives and "serialize" when converting to string, for practical purposes, we frequently use the word "serialize"
to refer to both of these situations, even though it does not always imply conversion to a string or bytes.
model.model_dump(...)
¶
??? API "Документация по API" pydantic.main.BaseModel.model_dump
Это основной способ преобразования модели в словарь. Подмодели будут рекурсивно преобразованы в словари.
!!! Примечание. Единственным исключением из подмоделей, преобразуемых в словари, является то, что RootModel
и его подклассы будут иметь дамп значения root
поля напрямую, без словаря-обертки. Это также делается рекурсивно.
!!! note Примечание. Вычисляемые поля можно использовать для включения данных property
и cached_property
в выходные данные model.model_dump(...)
.
Пример:
from typing import Any, List, Optional
from pydantic import BaseModel, Field, Json
class BarModel(BaseModel):
whatever: int
class FooBarModel(BaseModel):
banana: Optional[float] = 1.1
foo: str = Field(serialization_alias='foo_alias')
bar: BarModel
m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})
# returns a dictionary:
print(m.model_dump())
#> {'banana': 3.14, 'foo': 'hello', 'bar': {'whatever': 123}}
print(m.model_dump(include={'foo', 'bar'}))
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(m.model_dump(exclude={'foo', 'bar'}))
#> {'banana': 3.14}
print(m.model_dump(by_alias=True))
#> {'banana': 3.14, 'foo_alias': 'hello', 'bar': {'whatever': 123}}
print(
FooBarModel(foo='hello', bar={'whatever': 123}).model_dump(
exclude_unset=True
)
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
FooBarModel(banana=1.1, foo='hello', bar={'whatever': 123}).model_dump(
exclude_defaults=True
)
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
FooBarModel(foo='hello', bar={'whatever': 123}).model_dump(
exclude_defaults=True
)
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
FooBarModel(banana=None, foo='hello', bar={'whatever': 123}).model_dump(
exclude_none=True
)
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
class Model(BaseModel):
x: List[Json[Any]]
print(Model(x=['{"a": 1}', '[1, 2]']).model_dump())
#> {'x': [{'a': 1}, [1, 2]]}
print(Model(x=['{"a": 1}', '[1, 2]']).model_dump(round_trip=True))
#> {'x': ['{"a":1}', '[1,2]']}
model.model_dump_json(...)
¶
??? API "Документация по API" pydantic.main.BaseModel.model_dump_json
Метод .model_dump_json()
сериализует модель непосредственно в строку в кодировке JSON, которая эквивалентна результату, полученному с помощью .model_dump()
.
См. аргументы для получения дополнительной информации.
!!! Примечание. Pydantic может сериализовать в JSON многие часто используемые типы, которые в противном случае были бы несовместимы с простым json.dumps(foobar)
(например, datetime
, date
или UUID
).
from datetime import datetime
from pydantic import BaseModel
class BarModel(BaseModel):
whatever: int
class FooBarModel(BaseModel):
foo: datetime
bar: BarModel
m = FooBarModel(foo=datetime(2032, 6, 1, 12, 13, 14), bar={'whatever': 123})
print(m.model_dump_json())
#> {"foo":"2032-06-01T12:13:14","bar":{"whatever":123}}
print(m.model_dump_json(indent=2))
"""
{
"foo": "2032-06-01T12:13:14",
"bar": {
"whatever": 123
}
}
"""
dict(model)
и итерация¶
Модели Pydantic также можно преобразовать в словари с помощью dict(model)
, а также можно перебирать поля модели с помощью for field_name, field_value in model:
. При таком подходе возвращаются необработанные значения полей, поэтому подмодели не будут преобразованы в словари.
Пример:
from pydantic import BaseModel
class BarModel(BaseModel):
whatever: int
class FooBarModel(BaseModel):
banana: float
foo: str
bar: BarModel
m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})
print(dict(m))
#> {'banana': 3.14, 'foo': 'hello', 'bar': BarModel(whatever=123)}
for name, value in m:
print(f'{name}: {value}')
#> banana: 3.14
#> foo: hello
#> bar: whatever=123
Также обратите внимание, что RootModel
преобразуется в словарь с помощью ключа 'root'
.
Пользовательские сериализаторы¶
Pydantic предоставляет несколько функциональных сериализаторов для настройки сериализации модели в словарь или JSON.
Сериализацию можно настроить для поля с помощью декоратора @field_serializer
и для модели с помощью декоратора @model_serializer
.
from datetime import datetime, timedelta, timezone
from typing import Any, Dict
from pydantic import BaseModel, ConfigDict, field_serializer, model_serializer
class WithCustomEncoders(BaseModel):
model_config = ConfigDict(ser_json_timedelta='iso8601')
dt: datetime
diff: timedelta
@field_serializer('dt')
def serialize_dt(self, dt: datetime, _info):
return dt.timestamp()
m = WithCustomEncoders(
dt=datetime(2032, 6, 1, tzinfo=timezone.utc), diff=timedelta(hours=100)
)
print(m.model_dump_json())
#> {"dt":1969660800.0,"diff":"P4DT4H"}
class Model(BaseModel):
x: str
@model_serializer
def ser_model(self) -> Dict[str, Any]:
return {'x': f'serialized {self.x}'}
print(Model(x='test value').model_dump_json())
#> {"x":"serialized test value"}
!!! Примечание. Один сериализатор также можно вызвать для всех полей, передав специальное значение '*' декоратору @field_serializer
.
Кроме того, PlainSerializer
и WrapSerializer
позволяют использовать функцию для изменения вывода сериализации.
Оба сериализатора принимают необязательные аргументы, включая:
return_type
указывает тип возвращаемого значения для функции. Если он опущен, он будет выведен из аннотации типа.when_used
указывает, когда следует использовать этот сериализатор. Принимает строку со значениями «всегда», «если-нет», «json» и «json-если-нет». По умолчанию «всегда».
PlainSerializer
использует простую функцию для изменения вывода сериализации.
from typing_extensions import Annotated
from pydantic import BaseModel
from pydantic.functional_serializers import PlainSerializer
FancyInt = Annotated[
int, PlainSerializer(lambda x: f'{x:,}', return_type=str, when_used='json')
]
class MyModel(BaseModel):
x: FancyInt
print(MyModel(x=1234).model_dump())
#> {'x': 1234}
print(MyModel(x=1234).model_dump(mode='json'))
#> {'x': '1,234'}
WrapSerializer
получает необработанные входные данные вместе с функцией-обработчиком, которая применяет стандартную логику сериализации и может изменять результирующее значение перед возвратом его в качестве окончательного результата сериализации.
from typing import Any
from typing_extensions import Annotated
from pydantic import BaseModel, SerializerFunctionWrapHandler
from pydantic.functional_serializers import WrapSerializer
def ser_wrap(v: Any, nxt: SerializerFunctionWrapHandler) -> str:
return f'{nxt(v + 1):,}'
FancyInt = Annotated[int, WrapSerializer(ser_wrap, when_used='json')]
class MyModel(BaseModel):
x: FancyInt
print(MyModel(x=1234).model_dump())
#> {'x': 1234}
print(MyModel(x=1234).model_dump(mode='json'))
#> {'x': '1,235'}
Переопределение типа возвращаемого значения при дампе модели¶
Хотя возвращаемое значение .model_dump()
обычно можно описать как dict[str, Any]
, с помощью @model_serializer
вы действительно можете заставить его возвращать значение, которое не соответствует этой сигнатуре:
from pydantic import BaseModel, model_serializer
class Model(BaseModel):
x: str
@model_serializer
def ser_model(self) -> str:
return self.x
print(Model(x='not a dict').model_dump())
#> not a dict
Если вы хотите сделать это и при этом получить правильную проверку типов для этого метода, вы можете переопределить .model_dump()
в блоке if TYPE_CHECKING:
from typing import TYPE_CHECKING, Any
from typing_extensions import Literal
from pydantic import BaseModel, model_serializer
class Model(BaseModel):
x: str
@model_serializer
def ser_model(self) -> str:
return self.x
if TYPE_CHECKING:
# Ensure type checkers see the correct return type
def model_dump(
self,
*,
mode: Literal['json', 'python'] | str = 'python',
include: Any = None,
exclude: Any = None,
by_alias: bool = False,
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
round_trip: bool = False,
warnings: bool = True,
) -> str: ...
Этот трюк на самом деле используется в RootModel
именно для этой цели.
Сериализация подклассов¶
Подклассы стандартных типов¶
Подклассы стандартных типов автоматически сбрасываются, как и их суперклассы:
from datetime import date, timedelta
from typing import Any, Type
from pydantic_core import core_schema
from pydantic import BaseModel, GetCoreSchemaHandler
class DayThisYear(date):
"""
Contrived example of a special type of date that
takes an int and interprets it as a day in the current year
"""
@classmethod
def __get_pydantic_core_schema__(
cls, source: Type[Any], handler: GetCoreSchemaHandler
) -> core_schema.CoreSchema:
return core_schema.no_info_after_validator_function(
cls.validate,
core_schema.int_schema(),
serialization=core_schema.format_ser_schema('%Y-%m-%d'),
)
@classmethod
def validate(cls, v: int):
return date(2023, 1, 1) + timedelta(days=v)
class FooModel(BaseModel):
date: DayThisYear
m = FooModel(date=300)
print(m.model_dump_json())
#> {"date":"2023-10-28"}
Экземпляры подклассов для полей BaseModel
, классов данных, TypedDict
¶
При использовании полей, аннотации которых сами являются структуроподобными типами (например, подклассы BaseModel
, классы данных и т. д.), поведением по умолчанию является сериализация значения атрибута, как если бы оно было экземпляром аннотированного типа, даже если это подкласс. Точнее, в дамп объекта будут включены только поля аннотированного типа:
from pydantic import BaseModel
class User(BaseModel):
name: str
class UserLogin(User):
password: str
class OuterModel(BaseModel):
user: User
user = UserLogin(name='pydantic', password='hunter2')
m = OuterModel(user=user)
print(m)
#> user=UserLogin(name='pydantic', password='hunter2')
print(m.model_dump()) # note: the password field is not included
#> {'user': {'name': 'pydantic'}}
!!! предупреждение «Предупреждение о миграции» Такое поведение отличается от того, как все работало в Pydantic V1, где мы всегда включали все поля (подкласса) при рекурсивном выгрузке моделей в словари. Мотивация этого изменения в поведении заключается в том, что оно помогает гарантировать, что вы точно знаете, какие поля могут быть включены при сериализации, даже если подклассы передаются при создании экземпляра объекта. В частности, это может помочь предотвратить неожиданности при добавлении конфиденциальной информации, такой как секреты, в поля подклассов.
Сериализация с помощью утиного набора 🦆¶
!!! вопрос «Что такое сериализация с утиной типизацией?»
Duck-typing serialization is the behavior of serializing an object based on the fields present in the object itself,
rather than the fields present in the schema of the object. This means that when an object is serialized, fields present in
a subclass, but not in the original schema, will be included in the serialized output.
This behavior was the default in Pydantic V1, but was changed in V2 to help ensure that you know precisely which
fields would be included when serializing, even if subclasses get passed when instantiating the object. This helps
prevent security risks when serializing subclasses with sensitive information, for example.
Если вам нужно поведение сериализации с утиной типизацией в стиле v1, вы можете использовать настройку времени выполнения или аннотировать отдельные типы.
- Уровень поля/типа: используйте аннотацию
SerializeAsAny
- Уровень времени выполнения: используйте флаг
serialize_as_any
при вызовеmodel_dump()
илиmodel_dump_json()
Подробнее об этих вариантах мы поговорим ниже:
Аннотация SerializeAsAny
:¶
Если вам нужно поведение сериализации с утиным типом, это можно сделать с помощью аннотации SerializeAsAny
для типа:
from pydantic import BaseModel, SerializeAsAny
class User(BaseModel):
name: str
class UserLogin(User):
password: str
class OuterModel(BaseModel):
as_any: SerializeAsAny[User]
as_user: User
user = UserLogin(name='pydantic', password='password')
print(OuterModel(as_any=user, as_user=user).model_dump())
"""
{
'as_any': {'name': 'pydantic', 'password': 'password'},
'as_user': {'name': 'pydantic'},
}
"""
Когда поле помечено как SerializeAsAny[<SomeType>]
, поведение проверки будет таким же, как если бы оно было помечено как <SomeType>
, а средства проверки типов, такие как mypy, также будут рассматривать атрибут как имеющий соответствующий тип. Но при сериализации поле будет сериализовано так, как если бы подсказка типа для поля была Any
, отсюда и название.
параметр serialize_as_any
времени выполнения¶
Параметр среды выполнения serialize_as_any
можно использовать для сериализации данных модели с поведением сериализации типа «утка» или без него. serialize_as_any
можно передать в качестве аргумента ключевого слова в методы model_dump()
и model_dump_json
классов BaseModel
и RootModel
. Его также можно передать в качестве аргумента ключевого слова методам dump_python()
и dump_json()
TypeAdapter
s.
Если для serialize_as_any
установлено значение True
, модель будет сериализована с использованием поведения сериализации утиного типа. Это означает, что модель будет игнорировать схему и вместо этого запрашивать сам объект, как его следует сериализовать. В частности, это означает, что при сериализации подклассов модели будут включены поля, присутствующие в подклассе, но не в исходной схеме.
Если для serialize_as_any
установлено значение False
(по умолчанию), модель будет сериализована с использованием схемы, а это означает, что поля, присутствующие в подклассе, но не в исходной схеме, будут игнорироваться.
!!! вопрос «Почему этот флаг полезен?» Иногда необходимо убедиться, что какие бы поля ни были добавлены в подклассы, сериализованный объект будет содержать только поля, перечисленные в исходном определении типа. Это может быть полезно, если вы добавите что-то вроде password: str
в подкласс, который вы не хотите случайно включать в сериализованный вывод.
Например:
from pydantic import BaseModel
class User(BaseModel):
name: str
class UserLogin(User):
password: str
class OuterModel(BaseModel):
user1: User
user2: User
user = UserLogin(name='pydantic', password='password')
outer_model = OuterModel(user1=user, user2=user)
print(outer_model.model_dump(serialize_as_any=True)) # (1)!
"""
{
'user1': {'name': 'pydantic', 'password': 'password'},
'user2': {'name': 'pydantic', 'password': 'password'},
}
"""
print(outer_model.model_dump(serialize_as_any=False)) # (2)!
#> {'user1': {'name': 'pydantic'}, 'user2': {'name': 'pydantic'}}
- Если для
serialize_as_any
установлено значениеTrue
, результат соответствует результату V1. - Если для
serialize_as_any
установлено значениеFalse
(по умолчанию в V2), поля, присутствующие в подклассе, но не в базовом классе, не включаются в сериализацию.
Этот параметр действует даже для вложенных и рекурсивных шаблонов. Например:
from typing import List
from pydantic import BaseModel
class User(BaseModel):
name: str
friends: List['User']
class UserLogin(User):
password: str
class OuterModel(BaseModel):
user: User
user = UserLogin(
name='samuel',
password='pydantic-pw',
friends=[UserLogin(name='sebastian', password='fastapi-pw', friends=[])],
)
print(OuterModel(user=user).model_dump(serialize_as_any=True)) # (1)!
"""
{
'user': {
'name': 'samuel',
'friends': [
{'name': 'sebastian', 'friends': [], 'password': 'fastapi-pw'}
],
'password': 'pydantic-pw',
}
}
"""
print(OuterModel(user=user).model_dump(serialize_as_any=False)) # (2)!
"""
{'user': {'name': 'samuel', 'friends': [{'name': 'sebastian', 'friends': []}]}}
"""
- Даже вложенные экземпляры модели
User
сохраняются с полями, уникальными для подклассовUser
. - Даже вложенные экземпляры модели
User
выгружаются без полей, уникальных для подклассовUser
.
!!! note Примечание. Поведение флага времени выполнения serialize_as_any
почти такое же, как и поведение аннотации SerializeAsAny
. Есть несколько нюансов, над которыми мы работаем, но по большей части вы можете ожидать одинакового поведения от обоих. Подробнее о различиях читайте в этом активном выпуске.
Переопределение значения по умолчанию serialize_as_any
(False)¶
Вы можете переопределить настройку по умолчанию для serialize_as_any
, настроив подкласс BaseModel
, который переопределяет значение по умолчанию для serialize_as_any
для model_dump()
и model_dump_json()
, а затем использовать его в качестве базового класса (вместо pydantic.BaseModel
) для любой вашей модели. хочу иметь это поведение по умолчанию.
Например, вы можете сделать следующее, если хотите использовать сериализацию с утиной типизацией по умолчанию:
from typing import Any, Dict
from pydantic import BaseModel, SecretStr
class MyBaseModel(BaseModel):
def model_dump(self, **kwargs) -> Dict[str, Any]:
return super().model_dump(serialize_as_any=True, **kwargs)
def model_dump_json(self, **kwargs) -> str:
return super().model_dump_json(serialize_as_any=True, **kwargs)
class User(MyBaseModel):
name: str
class UserInfo(User):
password: SecretStr
class OuterModel(MyBaseModel):
user: User
u = OuterModel(user=UserInfo(name='John', password='secret_pw'))
print(u.model_dump_json()) # (1)!
#> {"user":{"name":"John","password":"**********"}}
- По умолчанию
model_dump_json
будет использовать поведение сериализации утиного типа, что означает, что полеpassword
включается в выходные данные.
pickle.dumps(model)
¶
Модели Pydantic поддерживают эффективное травление и расмаринование.
import pickle
from pydantic import BaseModel
class FooBarModel(BaseModel):
a: str
b: int
m = FooBarModel(a='hello', b=123)
print(m)
#> a='hello' b=123
data = pickle.dumps(m)
print(data[:20])
#> b'\x80\x04\x95\x95\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main_'
m2 = pickle.loads(data)
print(m2)
#> a='hello' b=123
Расширенное включение и исключение¶
Методы model_dump
и model_dump_json
поддерживают аргументы include
и exclude
, которые могут быть наборами или словарями. Это позволяет вложенный выбор полей для экспорта:
from pydantic import BaseModel, SecretStr
class User(BaseModel):
id: int
username: str
password: SecretStr
class Transaction(BaseModel):
id: str
user: User
value: int
t = Transaction(
id='1234567890',
user=User(id=42, username='JohnDoe', password='hashedpassword'),
value=9876543210,
)
# using a set:
print(t.model_dump(exclude={'user', 'value'}))
#> {'id': '1234567890'}
# using a dict:
print(t.model_dump(exclude={'user': {'username', 'password'}, 'value': True}))
#> {'id': '1234567890', 'user': {'id': 42}}
print(t.model_dump(include={'id': True, 'user': {'id'}}))
#> {'id': '1234567890', 'user': {'id': 42}}
True
указывает, что мы хотим исключить или включить весь ключ, как если бы мы включили его в набор. Это можно сделать на любом уровне глубины.
Особую осторожность необходимо соблюдать при включении или исключении полей из списка или кортежа подмоделей или словарей. В этом сценарии model_dump
и связанные с ним методы ожидают целочисленные ключи для поэлементного включения или исключения. Чтобы исключить поле из каждого члена списка или кортежа, можно использовать словарный ключ '__all__'
, как показано здесь:
import datetime
from typing import List
from pydantic import BaseModel, SecretStr
class Country(BaseModel):
name: str
phone_code: int
class Address(BaseModel):
post_code: int
country: Country
class CardDetails(BaseModel):
number: SecretStr
expires: datetime.date
class Hobby(BaseModel):
name: str
info: str
class User(BaseModel):
first_name: str
second_name: str
address: Address
card_details: CardDetails
hobbies: List[Hobby]
user = User(
first_name='John',
second_name='Doe',
address=Address(
post_code=123456, country=Country(name='USA', phone_code=1)
),
card_details=CardDetails(
number='4212934504460000', expires=datetime.date(2020, 5, 1)
),
hobbies=[
Hobby(name='Programming', info='Writing code and stuff'),
Hobby(name='Gaming', info='Hell Yeah!!!'),
],
)
exclude_keys = {
'second_name': True,
'address': {'post_code': True, 'country': {'phone_code'}},
'card_details': True,
# You can exclude fields from specific members of a tuple/list by index:
'hobbies': {-1: {'info'}},
}
include_keys = {
'first_name': True,
'address': {'country': {'name'}},
'hobbies': {0: True, -1: {'name'}},
}
# would be the same as user.model_dump(exclude=exclude_keys) in this case:
print(user.model_dump(include=include_keys))
"""
{
'first_name': 'John',
'address': {'country': {'name': 'USA'}},
'hobbies': [
{'name': 'Programming', 'info': 'Writing code and stuff'},
{'name': 'Gaming'},
],
}
"""
# To exclude a field from all members of a nested list or tuple, use "__all__":
print(user.model_dump(exclude={'hobbies': {'__all__': {'info'}}}))
"""
{
'first_name': 'John',
'second_name': 'Doe',
'address': {
'post_code': 123456,
'country': {'name': 'USA', 'phone_code': 1},
},
'card_details': {
'number': SecretStr('**********'),
'expires': datetime.date(2020, 5, 1),
},
'hobbies': [{'name': 'Programming'}, {'name': 'Gaming'}],
}
"""
То же самое относится и к методу model_dump_json
.
Включение и исключение на уровне модели и поля¶
В дополнение к явным аргументам exclude
и include
передаваемым в методы model_dump
и model_dump_json
, мы также можем передать аргументы exclude: bool
непосредственно в конструктор Field
:
Установка exclude
в конструкторе поля ( Field(..., exclude=True)
) имеет приоритет над exclude
/ include
в model_dump
и model_dump_json
:
from pydantic import BaseModel, Field, SecretStr
class User(BaseModel):
id: int
username: str
password: SecretStr = Field(..., exclude=True)
class Transaction(BaseModel):
id: str
value: int = Field(exclude=True)
t = Transaction(
id='1234567890',
value=9876543210,
)
print(t.model_dump())
#> {'id': '1234567890'}
print(t.model_dump(include={'id': True, 'value': True})) # (1)!
#> {'id': '1234567890'}
value
исключено из вывода, поскольку оно исключено вField
.
При этом установка exclude
в конструкторе поля ( Field(..., exclude=True)
) не имеет приоритета над параметрами exclude_unset
, exclude_none
и exclude_default
в model_dump
и model_dump_json
:
from typing import Optional
from pydantic import BaseModel, Field
class Person(BaseModel):
name: str
age: Optional[int] = Field(None, exclude=False)
person = Person(name='Jeremy')
print(person.model_dump())
#> {'name': 'Jeremy', 'age': None}
print(person.model_dump(exclude_none=True)) # (1)!
#> {'name': 'Jeremy'}
print(person.model_dump(exclude_unset=True)) # (2)!
#> {'name': 'Jeremy'}
print(person.model_dump(exclude_defaults=True)) # (3)!
#> {'name': 'Jeremy'}
age
исключен из выходных данных, поскольку дляexclude_none
установлено значениеTrue
, аage
—None
.age
исключен из выходных данных, поскольку дляexclude_unset
установлено значениеTrue
, аage
не был установлен в конструкторе Person.age
исключен из выходных данных, поскольку дляexclude_defaults
установлено значениеTrue
, аage
принимает значение по умолчаниюNone
.
Контекст сериализации¶
Вы можете передать объект контекста методам сериализации, доступ к которому можно получить из аргумента info
для декорированных функций сериализатора. Это полезно, когда вам нужно динамически обновлять поведение сериализации во время выполнения. Например, если вы хотите, чтобы поле выгружалось в зависимости от динамически управляемого набора разрешенных значений, это можно сделать, передав разрешенные значения по контексту:
from pydantic import BaseModel, SerializationInfo, field_serializer
class Model(BaseModel):
text: str
@field_serializer('text')
def remove_stopwords(self, v: str, info: SerializationInfo):
context = info.context
if context:
stopwords = context.get('stopwords', set())
v = ' '.join(w for w in v.split() if w.lower() not in stopwords)
return v
model = Model.model_construct(**{'text': 'This is an example document'})
print(model.model_dump()) # no context
#> {'text': 'This is an example document'}
print(model.model_dump(context={'stopwords': ['this', 'is', 'an']}))
#> {'text': 'example document'}
print(model.model_dump(context={'stopwords': ['document']}))
#> {'text': 'This is an example'}
Аналогичным образом вы можете использовать контекст для проверки .
model_copy(...)
¶
??? API "Документация по API" pydantic.main.BaseModel.model_copy
model_copy()
позволяет дублировать модели (с дополнительными обновлениями), что особенно полезно при работе с замороженными моделями.
Пример:
from pydantic import BaseModel
class BarModel(BaseModel):
whatever: int
class FooBarModel(BaseModel):
banana: float
foo: str
bar: BarModel
m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})
print(m.model_copy(update={'banana': 0}))
#> banana=0 foo='hello' bar=BarModel(whatever=123)
print(id(m.bar) == id(m.model_copy().bar))
#> True
# normal copy gives the same object reference for bar
print(id(m.bar) == id(m.model_copy(deep=True).bar))
#> False
# deep copy gives a new object reference for `bar`
本文总阅读量次