??? api "API 문서" pydantic.main.BaseModel
Pydantic에서 스키마를 정의하는 주요 방법 중 하나는 모델을 이용하는 것입니다. 모델은 단순히 pydantic.BaseModel
을 상속하고 필드를 주석이 달린 속성으로 정의하는 클래스입니다.
모델은 C와 같은 언어의 구조체와 유사하거나 API의 단일 엔드포인트 요구 사항으로 생각할 수 있습니다.
모델은 Python의 데이터 클래스와 많은 유사점을 공유하지만 유효성 검사, 직렬화 및 JSON 스키마 생성과 관련된 특정 워크플로를 간소화하는 몇 가지 미묘하면서도 중요한 차이점을 갖도록 설계되었습니다. 문서의 데이터 클래스 섹션에서 이에 대한 자세한 내용을 찾을 수 있습니다.
신뢰할 수 없는 데이터가 모델에 전달될 수 있으며, 구문 분석 및 검증 후 Pydantic은 결과 모델 인스턴스의 필드가 모델에 정의된 필드 유형을 준수함을 보장합니다.
!!! "검증 — 의도적인 잘못된 이름"을 참고하세요. ### TL;DR
We use the term "validation" to refer to the process of instantiating a model (or other type) that adheres to specified types and
constraints. This task, which Pydantic is well known for, is most widely recognized as "validation" in colloquial terms,
even though in other contexts the term "validation" may be more restrictive.
---
### The long version
The potential confusion around the term "validation" arises from the fact that, strictly speaking, Pydantic's
primary focus doesn't align precisely with the dictionary definition of "validation":
> ### validation
> _noun_
> the action of checking or proving the validity or accuracy of something.
In Pydantic, the term "validation" refers to the process of instantiating a model (or other type) that adheres to specified
types and constraints. Pydantic guarantees the types and constraints of the output, not the input data.
This distinction becomes apparent when considering that Pydantic's `ValidationError` is raised
when data cannot be successfully parsed into a model instance.
While this distinction may initially seem subtle, it holds practical significance.
In some cases, "validation" goes beyond just model creation, and can include the copying and coercion of data.
This can involve copying arguments passed to the constructor in order to perform coercion to a new type
without mutating the original input data. For a more in-depth understanding of the implications for your usage,
refer to the [Data Conversion](#data-conversion) and [Attribute Copies](#attribute-copies) sections below.
In essence, Pydantic's primary goal is to assure that the resulting structure post-processing (termed "validation")
precisely conforms to the applied type hints. Given the widespread adoption of "validation" as the colloquial term
for this process, we will consistently use it in our documentation.
While the terms "parse" and "validation" were previously used interchangeably, moving forward, we aim to exclusively employ "validate",
with "parse" reserved specifically for discussions related to [JSON parsing](../concepts/json.md).
기본 모델 사용법¶
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str = 'Jane Doe'
이 예에서 User
두 개의 필드가 있는 모델입니다.
id
는 정수이며 필수입니다.name
- 문자열이며 필수는 아닙니다(기본값이 있음).
사용자 = 사용자(id='123')
이 예에서 user
User
의 인스턴스입니다. 개체를 초기화하면 모든 구문 분석 및 유효성 검사가 수행됩니다. ValidationError
발생하지 않으면 결과 모델 인스턴스가 유효한 것입니다.
assert user.id == 123
assert isinstance(user.id, int)
# Note that '123' was coerced to an int and its value is 123
Pydantic의 강제 논리에 대한 자세한 내용은 데이터 변환 에서 확인할 수 있습니다. 모델의 필드는 user
개체의 일반 속성으로 액세스할 수 있습니다. 문자열 '123'
필드 유형에 따라 int로 변환되었습니다.
assert user.name == 'Jane Doe'
name
user
초기화 시 설정되지 않았으므로 기본값을 갖습니다.
assert user.model_fields_set == {'id'}
사용자가 초기화될 때 제공된 필드입니다.
assert user.model_dump() == {'id': 123, 'name': 'Jane Doe'}
.model_dump()
또는 dict(user)
는 필드 사전을 제공하지만 .model_dump()
다양한 다른 인수를 사용할 수 있습니다. ( dict(user)
는 중첩된 모델을 dict로 재귀적으로 변환하지 않지만 .model_dump()
변환합니다.)
user.id = 321
assert user.id == 321
기본적으로 모델은 변경 가능하며 속성 할당을 통해 필드 값을 변경할 수 있습니다.
모델 방법 및 속성¶
위의 예는 모델이 수행할 수 있는 작업의 빙산의 일각만을 보여줍니다. 모델에는 다음과 같은 메서드와 속성이 있습니다.
model_computed_fields
: 이 모델 인스턴스의 계산된 필드 사전입니다.model_construct()
: 유효성 검사를 실행하지 않고 모델을 생성하기 위한 클래스 메서드입니다. 검증 없이 모델 생성을 참조하십시오.model_copy()
: 모델의 복사본(기본적으로 얕은 복사본)을 반환합니다. 직렬화를 참조하세요.model_dump()
: 모델의 필드와 값의 사전을 반환합니다. 직렬화를 참조하세요.model_dump_json()
:model_dump()
의 JSON 문자열 표현을 반환합니다. 직렬화를 참조하세요.model_extra
: 검증 중에 추가 필드 세트를 가져옵니다.model_fields_set
: 모델 인스턴스가 초기화될 때 설정된 필드 집합입니다.model_json_schema()
: 모델을 JSON 스키마로 나타내는 jsonable 사전을 반환합니다. JSON 스키마를 참조하세요.model_parametrized_name()
: 일반 클래스의 매개변수화를 위한 클래스 이름을 계산합니다.model_post_init()
: 모델 초기화 후 추가 초기화를 수행합니다.model_rebuild()
: 재귀 일반 모델 구축도 지원하는 모델 스키마를 다시 빌드합니다. 모델 스키마 재구축을 참조하세요.model_validate()
: 모든 객체를 모델에 로드하기 위한 유틸리티입니다. 도우미 기능을 참조하세요.model_validate_json()
: Pydantic 모델에 대해 주어진 JSON 데이터의 유효성을 검사하기 위한 유틸리티입니다. 도우미 기능을 참조하세요.
!!! note 전체 메소드 및 속성 목록을 포함하는 클래스 정의는 BaseModel
을 참조하세요.
!!! Tip Pydantic V1의 변경 사항에 대한 자세한 내용은 마이그레이션 가이드 의 pydantic.BaseModel
변경 사항을 참조하세요.
중첩 모델¶
모델 자체를 주석의 유형으로 사용하여 보다 복잡한 계층적 데이터 구조를 정의할 수 있습니다.
from typing import List, Optional
from pydantic import BaseModel
class Foo(BaseModel):
count: int
size: Optional[float] = None
class Bar(BaseModel):
apple: str = 'x'
banana: str = 'y'
class Spam(BaseModel):
foo: Foo
bars: List[Bar]
m = Spam(foo={'count': 4}, bars=[{'apple': 'x1'}, {'apple': 'x2'}])
print(m)
"""
foo=Foo(count=4, size=None) bars=[Bar(apple='x1', banana='y'), Bar(apple='x2', banana='y')]
"""
print(m.model_dump())
"""
{
'foo': {'count': 4, 'size': None},
'bars': [{'apple': 'x1', 'banana': 'y'}, {'apple': 'x2', 'banana': 'y'}],
}
"""
자체 참조 모델의 경우 연기된 주석을 참조하세요.
!!! note 모델을 정의할 때 필드 이름과 해당 유형, 이전에 정의한 모델 또는 가져온 라이브러리 간의 이름 지정 충돌에 주의하세요.
For example, the following would yield a validation error:
```py
from typing import Optional
from pydantic import BaseModel
class Boo(BaseModel):
int: Optional[int] = None
m = Boo(int=123) # errors
```
An error occurs since the field `int` is set to a default value of `None` and has the exact same name as its type, so both are interpreted to be `None`.
모델 스키마 재구축¶
모델 스키마는 model_rebuild()
를 사용하여 다시 빌드할 수 있습니다. 이는 재귀적인 일반 모델을 구축하는 데 유용합니다.
from pydantic import BaseModel, PydanticUserError
class Foo(BaseModel):
x: 'Bar'
try:
Foo.model_json_schema()
except PydanticUserError as e:
print(e)
"""
`Foo` is not fully defined; you should define `Bar`, then call `Foo.model_rebuild()`.
For further information visit https://errors.pydantic.dev/2/u/class-not-fully-defined
"""
class Bar(BaseModel):
pass
Foo.model_rebuild()
print(Foo.model_json_schema())
"""
{
'$defs': {'Bar': {'properties': {}, 'title': 'Bar', 'type': 'object'}},
'properties': {'x': {'$ref': '#/$defs/Bar'}},
'required': ['x'],
'title': 'Foo',
'type': 'object',
}
"""
Pydantic은 이것이 필요한 시기를 자동으로 결정하고 완료되지 않은 경우 오류를 발생시키려고 시도하지만 재귀 모델이나 제네릭을 처리할 때 사전에 model_rebuild()
를 호출하는 것이 좋습니다.
V2에서는 model_rebuild()
가 V1의 update_forward_refs()
대체했습니다. 새로운 동작에는 약간의 차이가 있습니다. 가장 큰 변화는 가장 바깥쪽 모델에서 model_rebuild()
를 호출할 때 전체 모델(중첩 모델 및 모든 모델)의 유효성 검사에 사용되는 핵심 스키마를 구축하므로 모든 유형이 전혀 model_rebuild()
가 호출되기 전에 레벨이 준비되어야 합니다.
임의 클래스 인스턴스¶
(이전 명칭은 "ORM 모드"/ from_orm
입니다.)
Pydantic 모델은 모델 필드 이름에 해당하는 인스턴스 속성을 읽어 임의의 클래스 인스턴스에서 생성할 수도 있습니다. 이 기능의 일반적인 응용 프로그램 중 하나는 ORM(객체 관계형 매핑)과의 통합입니다.
이렇게 하려면 config 속성을 설정하십시오. model_config['from_attributes'] = True
. 자세한 내용은 모델 구성 및 ConfigDict를 참조하세요.
여기 예제에서는 SQLAlchemy를 사용하지만 모든 ORM에 동일한 접근 방식이 적용됩니다.
from typing import List
from sqlalchemy import Column, Integer, String
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.orm import declarative_base
from typing_extensions import Annotated
from pydantic import BaseModel, ConfigDict, StringConstraints
Base = declarative_base()
class CompanyOrm(Base):
__tablename__ = 'companies'
id = Column(Integer, primary_key=True, nullable=False)
public_key = Column(String(20), index=True, nullable=False, unique=True)
name = Column(String(63), unique=True)
domains = Column(ARRAY(String(255)))
class CompanyModel(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
public_key: Annotated[str, StringConstraints(max_length=20)]
name: Annotated[str, StringConstraints(max_length=63)]
domains: List[Annotated[str, StringConstraints(max_length=255)]]
co_orm = CompanyOrm(
id=123,
public_key='foobar',
name='Testing',
domains=['example.com', 'foobar.com'],
)
print(co_orm)
#> <__main__.CompanyOrm object at 0x0123456789ab>
co_model = CompanyModel.model_validate(co_orm)
print(co_model)
"""
id=123 public_key='foobar' name='Testing' domains=['example.com', 'foobar.com']
"""
예약된 이름¶
예약된 SQLAlchemy 필드 뒤에 Column
이름을 지정할 수 있습니다. 이 경우 Field
별칭이 편리합니다.
import typing
import sqlalchemy as sa
from sqlalchemy.orm import declarative_base
from pydantic import BaseModel, ConfigDict, Field
class MyModel(BaseModel):
model_config = ConfigDict(from_attributes=True)
metadata: typing.Dict[str, str] = Field(alias='metadata_')
Base = declarative_base()
class SQLModel(Base):
__tablename__ = 'my_table'
id = sa.Column('id', sa.Integer, primary_key=True)
# 'metadata' is reserved by SQLAlchemy, hence the '_'
metadata_ = sa.Column('metadata', sa.JSON)
sql_model = SQLModel(metadata_={'key': 'val'}, id=1)
pydantic_model = MyModel.model_validate(sql_model)
print(pydantic_model.model_dump())
#> {'metadata': {'key': 'val'}}
print(pydantic_model.model_dump(by_alias=True))
#> {'metadata_': {'key': 'val'}}
!!! note 위의 예는 필드 채우기 시 별칭이 필드 이름보다 우선하기 때문에 작동합니다. SQLModel
의 metadata
속성에 액세스하면 ValidationError
발생합니다.
중첩된 속성¶
속성을 사용하여 모델을 구문 분석할 때 모델 인스턴스는 최상위 속성과 더 깊게 중첩된 속성 모두에서 적절하게 생성됩니다.
다음은 원리를 보여주는 예입니다.
from typing import List
from pydantic import BaseModel, ConfigDict
class PetCls:
def __init__(self, *, name: str, species: str):
self.name = name
self.species = species
class PersonCls:
def __init__(self, *, name: str, age: float = None, pets: List[PetCls]):
self.name = name
self.age = age
self.pets = pets
class Pet(BaseModel):
model_config = ConfigDict(from_attributes=True)
name: str
species: str
class Person(BaseModel):
model_config = ConfigDict(from_attributes=True)
name: str
age: float = None
pets: List[Pet]
bones = PetCls(name='Bones', species='dog')
orion = PetCls(name='Orion', species='cat')
anna = PersonCls(name='Anna', age=20, pets=[bones, orion])
anna_model = Person.model_validate(anna)
print(anna_model)
"""
name='Anna' age=20.0 pets=[Pet(name='Bones', species='dog'), Pet(name='Orion', species='cat')]
"""
오류 처리¶
Pydantic은 검증 중인 데이터에서 오류를 발견할 때마다 ValidationError
발생시킵니다.
ValidationError
유형의 단일 예외는 발견된 오류 수에 관계없이 발생하며 해당 ValidationError
모든 오류와 오류 발생 방식에 대한 정보가 포함됩니다.
표준 및 사용자 정의 오류에 대한 자세한 내용은 오류 처리를 참조하세요.
데모로:
from typing import List
from pydantic import BaseModel, ValidationError
class Model(BaseModel):
list_of_ints: List[int]
a_float: float
data = dict(
list_of_ints=['1', 2, 'bad'],
a_float='not a float',
)
try:
Model(**data)
except ValidationError as e:
print(e)
"""
2 validation errors for Model
list_of_ints.2
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='bad', input_type=str]
a_float
Input should be a valid number, unable to parse string as a number [type=float_parsing, input_value='not a float', input_type=str]
"""
도우미 기능¶
Pydantic은 데이터 구문 분석을 위한 모델에 세 가지 classmethod
도우미 함수를 제공합니다.
model_validate()
: 이는 키워드 인수 대신 dict 또는 객체를 취한다는 점을 제외하면 모델의__init__
메서드와 매우 유사합니다. 전달된 객체의 유효성을 검사할 수 없거나 문제의 모델의 사전이나 인스턴스가 아닌 경우ValidationError
발생합니다.model_validate_json()
: 이것은 str 또는 바이트를 가져와 json 으로 구문 분석한 다음 결과를model_validate()
에 전달합니다.-
model_validate_strings()
: 문자열 키와 값이 포함된 dict(중첩 가능)를 취하고 해당 문자열이 올바른 유형으로 강제 변환될 수 있도록 json 모드에서 데이터의 유효성을 검사합니다.from datetime import datetime from typing import Optional
from pydantic import BaseModel, ValidationError
class User(BaseModel): id: int name: str = 'John Doe' signup_ts: Optional[datetime] = None
m = User.model_validate({'id': 123, 'name': 'James'}) print(m)
> id=123 name='James' signup_ts=None¶
try: User.model_validate(['not', 'a', 'dict']) except ValidationError as e: print(e) """ 1 validation error for User Input should be a valid dictionary or instance of User [type=model_type, input_value=['not', 'a', 'dict'], input_type=list] """
m = User.model_validate_json('{"id": 123, "name": "James"}') print(m)
> id=123 name='James' signup_ts=None¶
try: m = User.model_validate_json('{"id": 123, "name": 123}') except ValidationError as e: print(e) """ 1 validation error for User name Input should be a valid string [type=string_type, input_value=123, input_type=int] """
try: m = User.model_validate_json('invalid JSON') except ValidationError as e: print(e) """ 1 validation error for User Invalid JSON: expected value at line 1 column 1 [type=json_invalid, input_value='invalid JSON', input_type=str] """
m = User.model_validate_strings({'id': '123', 'name': 'James'}) print(m)
> id=123 name='James' signup_ts=None¶
m = User.model_validate_strings( {'id': '123', 'name': 'James', 'signup_ts': '2024-04-01T12:00:00'} ) print(m)
> id=123 name='James' signup_ts=datetime.datetime(2024, 4, 1, 12, 0)¶
try: m = User.model_validate_strings( {'id': '123', 'name': 'James', 'signup_ts': '2024-04-01'}, strict=True ) except ValidationError as e: print(e) """ 1 validation error for User signup_ts Input should be a valid datetime, invalid datetime separator, expected
T
,t
,_
or space [type=datetime_parsing, input_value='2024-04-01', input_type=str] """
JSON 이외의 형식으로 직렬화된 데이터의 유효성을 검사하려면 데이터를 직접 dict에 로드한 다음 model_validate
에 전달해야 합니다.
!!! note 관련된 유형 및 모델 구성에 따라 model_validate
및 model_validate_json
의 유효성 검사 동작이 다를 수 있습니다. JSON이 아닌 소스에서 오는 데이터가 있지만 model_validate_json
에서 얻을 수 있는 것과 동일한 유효성 검사 동작 및 오류를 원하는 경우 현재로서는 다음 중 하나를 사용하는 것이 좋습니다. model_validate_json(json.dumps(data))
, 또는 데이터가 문자열 키와 값이 포함된 (잠재적으로 중첩된) 사전 형식을 취하는 경우 model_validate_strings
를 사용하세요.
!!! note 문서의 JSON 섹션에서 JSON 구문 분석에 대해 자세히 알아보세요.
!!! note 모델 인스턴스를 model_validate
에 전달하는 경우 모델 구성에서 revalidate_instances
설정을 고려하는 것이 좋습니다. 이 값을 설정하지 않으면 모델 인스턴스에서 유효성 검사를 건너뜁니다. 아래 예를 참조하세요.
\=== "
revalidate_instances='never'
" ```pydantic import BaseModel의 py
class Model(BaseModel):
a: int
m = Model(a=0)
# note: the `model_config` setting validate_assignment=True` can prevent this kind of misbehavior
m.a = 'not an int'
# doesn't raise a validation error even though m is invalid
m2 = Model.model_validate(m)
```
\=== "
revalidate_instances='always'
" ```py from pydantic import BaseModel, ConfigDict, ValidationError
class Model(BaseModel):
a: int
model_config = ConfigDict(revalidate_instances='always')
m = Model(a=0)
# note: the `model_config` setting validate_assignment=True` can prevent this kind of misbehavior
m.a = 'not an int'
try:
m2 = Model.model_validate(m)
except ValidationError as e:
print(e)
"""
1 validation error for Model
a
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='not an int', input_type=str]
"""
```
검증 없이 모델 생성¶
Pydantic은 검증 없이 모델을 생성할 수 있는 model_construct()
메서드도 제공합니다. 이는 최소한 몇 가지 경우에 유용할 수 있습니다.
- 이미 유효한 것으로 알려진 복잡한 데이터로 작업할 때(성능상의 이유로)
- 하나 이상의 유효성 검사기 함수가 멱등성이 아닌 경우 또는
- 하나 이상의 유효성 검사기 기능에 트리거되고 싶지 않은 부작용이 있는 경우.
!!! note Pydantic V2에서는 BaseModel.__init__
과 BaseModel.model_construct
사이의 성능 격차가 상당히 줄어들었습니다. 간단한 모델의 경우 BaseModel.__init__
호출하는 것이 더 빠를 수도 있습니다. 성능상의 이유로 model_construct()
를 사용하는 경우 model_construct()
가 더 빠르다고 가정하기 전에 사용 사례를 프로파일링할 수 있습니다.
!!! 경고 model_construct()
는 유효성 검사를 수행하지 않습니다. 즉, 유효하지 않은 모델을 생성할 수 있습니다. 이미 검증되었거나 확실히 신뢰할 수 있는 데이터에만 model_construct()
메서드를 사용해야 합니다.
from pydantic import BaseModel
class User(BaseModel):
id: int
age: int
name: str = 'John Doe'
original_user = User(id=123, age=32)
user_data = original_user.model_dump()
print(user_data)
#> {'id': 123, 'age': 32, 'name': 'John Doe'}
fields_set = original_user.model_fields_set
print(fields_set)
#> {'age', 'id'}
# ...
# pass user_data and fields_set to RPC or save to the database etc.
# ...
# you can then create a new instance of User without
# re-running validation which would be unnecessary at this point:
new_user = User.model_construct(_fields_set=fields_set, **user_data)
print(repr(new_user))
#> User(id=123, age=32, name='John Doe')
print(new_user.model_fields_set)
#> {'age', 'id'}
# construct can be dangerous, only use it with validated data!:
bad_user = User.model_construct(id='dog')
print(repr(bad_user))
#> User(id='dog', name='John Doe')
model_construct()
에 대한 _fields_set
키워드 인수는 선택 사항이지만 원래 설정된 필드와 설정되지 않은 필드를 더 정확하게 확인할 수 있습니다. 생략하면 model_fields_set
는 제공된 데이터의 키가 됩니다.
예를 들어 위의 예에서 _fields_set
제공되지 않은 경우 new_user.model_fields_set
은 {'id', 'age', 'name'}
입니다.
RootModel
서브클래스의 경우 키워드 인수를 사용하는 대신 루트 값을 model_construct()
에 위치적으로 전달할 수 있습니다.
다음은 model_construct()
의 동작에 대한 몇 가지 추가 참고 사항입니다.
- "검증이 수행되지 않습니다"라고 말하면 여기에는 dict를 모델 인스턴스로 변환하는 것이 포함됩니다. 그래서 필드가 있다면
Model
유형을 사용하는 경우 내부 dict를model_construct()
에 전달하기 전에 직접 모델로 변환해야 합니다.- 특히,
model_construct()
메서드는 dicts에서 모델을 재귀적으로 구성하는 것을 지원하지 않습니다.
- 특히,
- 기본값이 있는 필드에 키워드 인수를 전달하지 않으면 기본값이 계속 사용됩니다.
- 비공개 속성이 있는 모델의 경우
__pydantic_private__
dict는__init__
호출할 때와 동일하게 초기화됩니다. model_construct()
를 사용하여 인스턴스를 생성할 때 사용자 정의__init__
메서드가 정의된 경우에도 모델이나 해당 상위 클래스의__init__
메서드가 호출되지 않습니다.
!!! note " model_construct
사용한 extra
동작에 대해" * 다음이 있는 모델의 경우 model_config['extra'] == 'allow'
, 필드에 해당하지 않는 데이터는 __pydantic_extra__
dict에 올바르게 저장되고 모델의 __dict__
에 저장됩니다. * 다음이 포함된 모델의 경우 model_config['extra'] == 'ignore'
, 필드에 해당하지 않는 데이터는 무시됩니다. 즉, 인스턴스의 __pydantic_extra__
또는 __dict__
에 저장되지 않습니다. * __init__
호출과 달리 model_construct
호출은 model_config['extra'] == 'forbid'
필드에 해당하지 않는 데이터가 있어도 오류가 발생하지 않습니다. 오히려, 상기 입력 데이터는 단순히 무시됩니다.
일반 모델¶
Pydantic은 공통 모델 구조를 더 쉽게 재사용할 수 있도록 일반 모델 생성을 지원합니다.
일반 모델을 선언하려면 다음 단계를 수행합니다.
- 모델을 매개변수화하는 데 사용할
typing.TypeVar
인스턴스를 하나 이상 선언합니다. pydantic.BaseModel
및typing.Generic
에서 상속되는 Pydantic 모델을 선언합니다. 여기서TypeVar
인스턴스를typing.Generic
에 대한 매개 변수로 전달합니다.TypeVar
인스턴스를 다른 유형이나 pydantic 모델로 대체하려는 주석으로 사용하십시오.
다음은 쉽게 재사용할 수 있는 HTTP 응답 페이로드 래퍼를 생성하기 위해 일반 BaseModel
하위 클래스를 사용하는 예입니다.
from typing import Generic, List, Optional, TypeVar
from pydantic import BaseModel, ValidationError
DataT = TypeVar('DataT')
class DataModel(BaseModel):
numbers: List[int]
people: List[str]
class Response(BaseModel, Generic[DataT]):
data: Optional[DataT] = None
print(Response[int](data=1))
#> data=1
print(Response[str](data='value'))
#> data='value'
print(Response[str](data='value').model_dump())
#> {'data': 'value'}
data = DataModel(numbers=[1, 2, 3], people=[])
print(Response[DataModel](data=data).model_dump())
#> {'data': {'numbers': [1, 2, 3], 'people': []}}
try:
Response[int](data='value')
except ValidationError as e:
print(e)
"""
1 validation error for Response[int]
data
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='value', input_type=str]
"""
model_config
설정하거나 일반 모델 정의에서 @field_validator
또는 기타 Pydantic 데코레이터를 사용하는 경우 BaseModel
하위 클래스에서 상속할 때와 동일한 방식으로 매개변수화된 하위 클래스에 적용됩니다. 일반 클래스에 정의된 모든 메서드도 상속됩니다.
Pydantic의 제네릭은 유형 검사기와도 적절하게 통합되므로 각 매개변수화에 대해 고유한 유형을 선언할 경우 예상할 수 있는 모든 유형 검사를 얻을 수 있습니다.
!!! note 내부적으로 Pydantic은 일반 모델이 매개변수화될 때 런타임에 BaseModel
의 하위 클래스를 생성합니다. 이러한 클래스는 캐시되므로 제네릭 모델 사용으로 인한 오버헤드가 최소화되어야 합니다.
일반 모델에서 상속하고 일반 모델이라는 사실을 유지하려면 서브클래스도 typing.Generic
에서 상속해야 합니다.
from typing import Generic, TypeVar
from pydantic import BaseModel
TypeX = TypeVar('TypeX')
class BaseClass(BaseModel, Generic[TypeX]):
X: TypeX
class ChildClass(BaseClass[TypeX], Generic[TypeX]):
# Inherit from Generic[TypeX]
pass
# Replace TypeX by int
print(ChildClass[int](X=1))
#> X=1
또한 슈퍼클래스의 유형 매개변수를 부분적으로 또는 완전히 대체하는 BaseModel
의 일반 하위 클래스를 생성할 수도 있습니다.
from typing import Generic, TypeVar
from pydantic import BaseModel
TypeX = TypeVar('TypeX')
TypeY = TypeVar('TypeY')
TypeZ = TypeVar('TypeZ')
class BaseClass(BaseModel, Generic[TypeX, TypeY]):
x: TypeX
y: TypeY
class ChildClass(BaseClass[int, TypeY], Generic[TypeY, TypeZ]):
z: TypeZ
# Replace TypeY by str
print(ChildClass[str, int](x='1', y='y', z='3'))
#> x=1 y='y' z=3
구체적인 하위 클래스의 이름이 중요한 경우 기본 이름 생성을 재정의할 수도 있습니다.
from typing import Any, Generic, Tuple, Type, TypeVar
from pydantic import BaseModel
DataT = TypeVar('DataT')
class Response(BaseModel, Generic[DataT]):
data: DataT
@classmethod
def model_parametrized_name(cls, params: Tuple[Type[Any], ...]) -> str:
return f'{params[0].__name__.title()}Response'
print(repr(Response[int](data=1)))
#> IntResponse(data=1)
print(repr(Response[str](data='a')))
#> StrResponse(data='a')
매개변수화된 일반 모델을 다른 모델의 유형으로 사용할 수 있습니다.
from typing import Generic, TypeVar
from pydantic import BaseModel
T = TypeVar('T')
class ResponseModel(BaseModel, Generic[T]):
content: T
class Product(BaseModel):
name: str
price: float
class Order(BaseModel):
id: int
product: ResponseModel[Product]
product = Product(name='Apple', price=0.5)
response = ResponseModel[Product](content=product)
order = Order(id=1, product=response)
print(repr(order))
"""
Order(id=1, product=ResponseModel[Product](content=Product(name='Apple', price=0.5)))
"""
!!! 팁 매개변수화된 일반 모델을 다른 모델의 유형으로 사용할 때(예: product: ResponseModel[Product]
), 모델 인스턴스를 초기화할 때 해당 일반 모델을 매개변수화해야 합니다(예: response = ResponseModel[Product](content=product)
). 그렇지 않으면 Pydantic이 전달된 데이터를 기반으로 일반 모델의 유형을 추론하지 않기 때문에 ValidationError
발생합니다.
중첩 모델에서 동일한 TypeVar
사용하면 모델의 여러 지점에서 입력 관계를 적용할 수 있습니다.
from typing import Generic, TypeVar
from pydantic import BaseModel, ValidationError
T = TypeVar('T')
class InnerT(BaseModel, Generic[T]):
inner: T
class OuterT(BaseModel, Generic[T]):
outer: T
nested: InnerT[T]
nested = InnerT[int](inner=1)
print(OuterT[int](outer=1, nested=nested))
#> outer=1 nested=InnerT[int](inner=1)
try:
nested = InnerT[str](inner='a')
print(OuterT[int](outer='a', nested=nested))
except ValidationError as e:
print(e)
"""
2 validation errors for OuterT[int]
outer
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
nested
Input should be a valid dictionary or instance of InnerT[int] [type=model_type, input_value=InnerT[str](inner='a'), input_type=InnerT[str]]
"""
바인딩된 유형 매개변수를 사용하고 유형 매개변수를 지정하지 않은 상태로 두면 Pydantic은 List
및 Dict
와 같은 내장 제네릭 유형을 처리하는 방식과 유사하게 일반 모델을 처리합니다.
- 일반 모델을 인스턴스화하기 전에 매개변수를 지정하지 않으면 해당 매개변수는
TypeVar
의 경계로 검증됩니다. - 관련된
TypeVar
에 경계가 없으면Any
로 처리됩니다.
또한 List
및 Dict
와 마찬가지로 TypeVar
사용하여 지정된 모든 매개변수는 나중에 구체적인 유형으로 대체될 수 있습니다.
from typing import Generic, TypeVar
from pydantic import BaseModel, ValidationError
AT = TypeVar('AT')
BT = TypeVar('BT')
class Model(BaseModel, Generic[AT, BT]):
a: AT
b: BT
print(Model(a='a', b='a'))
#> a='a' b='a'
IntT = TypeVar('IntT', bound=int)
typevar_model = Model[int, IntT]
print(typevar_model(a=1, b=1))
#> a=1 b=1
try:
typevar_model(a='a', b='a')
except ValidationError as exc:
print(exc)
"""
2 validation errors for Model[int, TypeVar]
a
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
b
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
"""
concrete_model = typevar_model[int]
print(concrete_model(a=1, b=1))
#> a=1 b=1
!!! 경고 오류가 발생하지 않을 수 있지만 isinstance 검사에서는 매개변수화된 제네릭을 사용하지 않는 것이 좋습니다.
For example, you should not do `isinstance(my_model, MyGenericModel[int])`. However, it is fine to do `isinstance(my_model, MyGenericModel)`. (Note that, for standard generics, it would raise an error to do a subclass check with a parameterized generic.)
If you need to perform isinstance checks against parametrized generics, you can do this by subclassing the parametrized generic class. This looks like `class MyIntModel(MyGenericModel[int]): ...` and `isinstance(my_model, MyIntModel)`.
Pydantic 모델이 TypeVar
경계에서 사용되고 일반 유형이 매개변수화되지 않은 경우 Pydantic은 검증을 위해 경계를 사용하지만 직렬화 측면에서 값을 Any
로 처리합니다.
from typing import Generic, Optional, TypeVar
from pydantic import BaseModel
class ErrorDetails(BaseModel):
foo: str
ErrorDataT = TypeVar('ErrorDataT', bound=ErrorDetails)
class Error(BaseModel, Generic[ErrorDataT]):
message: str
details: Optional[ErrorDataT]
class MyErrorDetails(ErrorDetails):
bar: str
# serialized as Any
error = Error(
message='We just had an error',
details=MyErrorDetails(foo='var', bar='var2'),
)
assert error.model_dump() == {
'message': 'We just had an error',
'details': {
'foo': 'var',
'bar': 'var2',
},
}
# serialized using the concrete parametrization
# note that `'bar': 'var2'` is missing
error = Error[ErrorDetails](
message='We just had an error',
details=ErrorDetails(foo='var'),
)
assert error.model_dump() == {
'message': 'We just had an error',
'details': {
'foo': 'var',
},
}
다음은 바인딩된 사양 및 일반 유형 매개변수화와 관련된 모든 순열을 열거하는 위 동작의 또 다른 예입니다.
from typing import Generic
from typing_extensions import TypeVar
from pydantic import BaseModel
TBound = TypeVar('TBound', bound=BaseModel)
TNoBound = TypeVar('TNoBound')
class IntValue(BaseModel):
value: int
class ItemBound(BaseModel, Generic[TBound]):
item: TBound
class ItemNoBound(BaseModel, Generic[TNoBound]):
item: TNoBound
item_bound_inferred = ItemBound(item=IntValue(value=3))
item_bound_explicit = ItemBound[IntValue](item=IntValue(value=3))
item_no_bound_inferred = ItemNoBound(item=IntValue(value=3))
item_no_bound_explicit = ItemNoBound[IntValue](item=IntValue(value=3))
# calling `print(x.model_dump())` on any of the above instances results in the following:
#> {'item': {'value': 3}}
default=...
(Python >= 3.13 또는 typing-extensions
통해 사용 가능) 또는 제약 조건( TypeVar('T', str, int)
; TypeVar
의 이 형식을 거의 사용하지 않는다는 점에 유의하세요)을 사용하는 경우 유형 변수가 매개변수화되지 않은 경우 기본값 또는 제약 조건은 유효성 검사와 직렬화 모두에 사용됩니다. pydantic.SerializeAsAny
를 사용하여 이 동작을 재정의할 수 있습니다.
from typing import Generic, Optional
from typing_extensions import TypeVar
from pydantic import BaseModel, SerializeAsAny
class ErrorDetails(BaseModel):
foo: str
ErrorDataT = TypeVar('ErrorDataT', default=ErrorDetails)
class Error(BaseModel, Generic[ErrorDataT]):
message: str
details: Optional[ErrorDataT]
class MyErrorDetails(ErrorDetails):
bar: str
# serialized using the default's serializer
error = Error(
message='We just had an error',
details=MyErrorDetails(foo='var', bar='var2'),
)
assert error.model_dump() == {
'message': 'We just had an error',
'details': {
'foo': 'var',
},
}
class SerializeAsAnyError(BaseModel, Generic[ErrorDataT]):
message: str
details: Optional[SerializeAsAny[ErrorDataT]]
# serialized as Any
error = SerializeAsAnyError(
message='We just had an error',
details=MyErrorDetails(foo='var', bar='baz'),
)
assert error.model_dump() == {
'message': 'We just had an error',
'details': {
'foo': 'var',
'bar': 'baz',
},
}
!!! note 참고, 제네릭 경계에 대해 유효성을 검사하는 경우 데이터 손실이 발생할 수 있는 경우 제네릭을 매개 변수화하지 않으면 약간의 문제가 발생할 수 있습니다. 아래 예를 참조하세요.
from typing import Generic
from typing_extensions import TypeVar
from pydantic import BaseModel
TItem = TypeVar('TItem', bound='ItemBase')
class ItemBase(BaseModel): ...
class IntItem(ItemBase):
value: int
class ItemHolder(BaseModel, Generic[TItem]):
item: TItem
loaded_data = {'item': {'value': 1}}
print(ItemHolder(**loaded_data).model_dump()) # (1)!
#> {'item': {}}
print(ItemHolder[IntItem](**loaded_data).model_dump()) # (2)!
#> {'item': {'value': 1}}
- 제네릭이 매개변수화되지 않은 경우 입력 데이터는 제네릭 경계에 대해 유효성이 검사됩니다.
ItemBase
에 필드가 없으면item
필드 정보가 손실됩니다. - 이 경우 런타임 유형 정보는 일반 매개변수화를 통해 명시적으로 제공되므로 입력 데이터는
IntItem
클래스에 대해 검증되고 직렬화 출력은 예상한 것과 일치합니다.
동적 모델 생성¶
??? api "API 문서" pydantic.main.create_model
필드를 지정하기 위해 런타임 정보를 사용하여 모델을 생성하는 것이 바람직한 경우가 있습니다. 이를 위해 Pydantic은 모델을 즉시 생성할 수 있도록 create_model
함수를 제공합니다:
from pydantic import BaseModel, create_model
DynamicFoobarModel = create_model(
'DynamicFoobarModel', foo=(str, ...), bar=(int, 123)
)
class StaticFoobarModel(BaseModel):
foo: str
bar: int = 123
여기서 StaticFoobarModel
과 DynamicFoobarModel
은 동일합니다.
필드는 다음 튜플 형식 중 하나로 정의됩니다.
(<type>, <default value>)
(<type>, Field(...))
typing.Annotated[<type>, Field(...)]
Field(...)
호출을 튜플(기본값)의 두 번째 인수로 사용하면 고급 필드 구성이 가능해집니다. 따라서 다음은 유사합니다.
from pydantic import BaseModel, Field, create_model
DynamicModel = create_model(
'DynamicModel',
foo=(str, Field(..., description='foo description', alias='FOO')),
)
class StaticModel(BaseModel):
foo: str = Field(..., description='foo description', alias='FOO')
특수 키워드 인수 __config__
및 __base__
사용하여 새 모델을 사용자 정의할 수 있습니다. 여기에는 추가 필드로 기본 모델을 확장하는 것이 포함됩니다.
from pydantic import BaseModel, create_model
class FooModel(BaseModel):
foo: str
bar: int = 123
BarModel = create_model(
'BarModel',
apple=(str, 'russet'),
banana=(str, 'yellow'),
__base__=FooModel,
)
print(BarModel)
#> <class '__main__.BarModel'>
print(BarModel.model_fields.keys())
#> dict_keys(['foo', 'bar', 'apple', 'banana'])
__validators__
인수에 사전을 전달하여 유효성 검사기를 추가할 수도 있습니다.
from pydantic import ValidationError, create_model, field_validator
def username_alphanumeric(cls, v):
assert v.isalnum(), 'must be alphanumeric'
return v
validators = {
'username_validator': field_validator('username')(username_alphanumeric)
}
UserModel = create_model(
'UserModel', username=(str, ...), __validators__=validators
)
user = UserModel(username='scolvin')
print(user)
#> username='scolvin'
try:
UserModel(username='scolvi%n')
except ValidationError as e:
print(e)
"""
1 validation error for UserModel
username
Assertion failed, must be alphanumeric [type=assertion_error, input_value='scolvi%n', input_type=str]
"""
!!! note 동적으로 생성된 모델을 피클하려면 다음을 수행하십시오.
- the model must be defined globally
- it must provide `__module__`
RootModel
및 사용자 정의 루트 유형¶
??? api "API 문서" pydantic.root_model.RootModel
Pydantic 모델은 pydantic.RootModel
을 서브클래싱하여 "사용자 정의 루트 유형"으로 정의할 수 있습니다.
루트 유형은 Pydantic에서 지원하는 모든 유형이 될 수 있으며 RootModel
에 대한 일반 매개변수로 지정됩니다. 루트 값은 첫 번째이자 유일한 인수를 통해 모델 __init__
또는 model_validate
에 전달될 수 있습니다.
이것이 어떻게 작동하는지에 대한 예는 다음과 같습니다.
from typing import Dict, List
from pydantic import RootModel
Pets = RootModel[List[str]]
PetsByName = RootModel[Dict[str, str]]
print(Pets(['dog', 'cat']))
#> root=['dog', 'cat']
print(Pets(['dog', 'cat']).model_dump_json())
#> ["dog","cat"]
print(Pets.model_validate(['dog', 'cat']))
#> root=['dog', 'cat']
print(Pets.model_json_schema())
"""
{'items': {'type': 'string'}, 'title': 'RootModel[List[str]]', 'type': 'array'}
"""
print(PetsByName({'Otis': 'dog', 'Milo': 'cat'}))
#> root={'Otis': 'dog', 'Milo': 'cat'}
print(PetsByName({'Otis': 'dog', 'Milo': 'cat'}).model_dump_json())
#> {"Otis":"dog","Milo":"cat"}
print(PetsByName.model_validate({'Otis': 'dog', 'Milo': 'cat'}))
#> root={'Otis': 'dog', 'Milo': 'cat'}
root
필드의 항목에 직접 액세스하거나 항목을 반복하려면 다음 예제와 같이 사용자 정의 __iter__
및 __getitem__
함수를 구현할 수 있습니다.
from typing import List
from pydantic import RootModel
class Pets(RootModel):
root: List[str]
def __iter__(self):
return iter(self.root)
def __getitem__(self, item):
return self.root[item]
pets = Pets.model_validate(['dog', 'cat'])
print(pets[0])
#> dog
print([pet for pet in pets])
#> ['dog', 'cat']
매개변수화된 루트 모델의 하위 클래스를 직접 만들 수도 있습니다.
from typing import List
from pydantic import RootModel
class Pets(RootModel[List[str]]):
def describe(self) -> str:
return f'Pets: {", ".join(self.root)}'
my_pets = Pets.model_validate(['dog', 'cat'])
print(my_pets.describe())
#> Pets: dog, cat
가짜 불변성¶
model_config['frozen'] = True
통해 모델을 변경할 수 없도록 구성할 수 있습니다. 이것이 설정된 경우 인스턴스 속성 값을 변경하려고 하면 오류가 발생합니다. 자세한 내용은 API 참조를 참조하세요.
!!! note 이 동작은 Pydantic V1에서 allow_mutation = False
구성 설정을 통해 달성되었습니다. 이 구성 플래그는 Pydantic V2에서 더 이상 사용되지 않으며 frozen
으로 대체되었습니다.
!!! 경고 Python에서는 불변성이 적용되지 않습니다. 개발자는 원하는 경우 일반적으로 "불변"으로 간주되는 개체를 수정할 수 있습니다.
from pydantic import BaseModel, ConfigDict, ValidationError
class FooBarModel(BaseModel):
model_config = ConfigDict(frozen=True)
a: str
b: dict
foobar = FooBarModel(a='hello', b={'apple': 'pear'})
try:
foobar.a = 'different'
except ValidationError as e:
print(e)
"""
1 validation error for FooBarModel
a
Instance is frozen [type=frozen_instance, input_value='different', input_type=str]
"""
print(foobar.a)
#> hello
print(foobar.b)
#> {'apple': 'pear'}
foobar.b['apple'] = 'grape'
print(foobar.b)
#> {'apple': 'grape'}
a
) 변경하려고 하면 오류가 발생했으며 a
(는) 변경되지 않은 상태로 유지됩니다. 그러나 dict b
변경 가능하며 foobar
의 불변성은 b
변경을 막지 않습니다.
추상 기본 클래스¶
Pydantic 모델은 Python의 ABC( 추상 기본 클래스 )와 함께 사용할 수 있습니다.
import abc
from pydantic import BaseModel
class FooBarModel(BaseModel, abc.ABC):
a: str
b: int
@abc.abstractmethod
def my_abstract_method(self):
pass
현장 주문¶
필드 순서는 다음과 같은 방식으로 모델에 영향을 미칩니다.
- 필드 순서는 모델 스키마 에 유지됩니다.
- 유효성 검사 오류 시 필드 순서가 유지됩니다.
-
필드 순서는
.model_dump()
및.model_dump_json()
등에 의해 유지됩니다.from pydantic import BaseModel, ValidationError
class Model(BaseModel): a: int b: int = 2 c: int = 1 d: int = 0 e: float
print(Model.model_fields.keys())
> dict_keys(['a', 'b', 'c', 'd', 'e'])¶
m = Model(e=2, a=1) print(m.model_dump())
>¶
try: Model(a='x', b='x', c='x', d='x', e='x') except ValidationError as err: error_locations = [e['loc'] for e in err.errors()]
print(error_locations)
> [('a',), ('b',), ('c',), ('d',), ('e',)]¶
필수 입력사항¶
필요에 따라 필드를 선언하려면 주석을 사용하거나 Field
사양과 결합된 주석을 사용하여 선언할 수 있습니다. 특히 Field
생성자를 사용할 때 필드가 필수임을 강조하기 위해 Ellipsis
/ ...
사용할 수도 있습니다.
Field
기능은 주로 속성에 대한 alias
이나 description
과 같은 설정을 구성하는 데 사용됩니다. 생성자는 Ellipsis
/ ...
를 유일한 위치 인수로 지원합니다. 이는 해당 필드가 필수임을 나타내는 방법으로 사용되지만 이 요구 사항을 적용하는 유형 힌트입니다.
from pydantic import BaseModel, Field
class Model(BaseModel):
a: int
b: int = ...
c: int = Field(..., alias='C')
여기서는 a
, b
및 c
모두 필요합니다. 그러나 이러한 b: int = ...
사용은 mypy 에서는 제대로 작동하지 않으며 v1.0 부터는 대부분의 경우 피해야 합니다.
!!! note Pydantic V1에서는 Optional
또는 Any
로 주석이 달린 필드에 기본값이 명시적으로 지정되지 않은 경우에도 묵시적으로 None
이라는 기본값이 부여됩니다. 이 동작은 Pydantic V2에서 변경되었으며 더 이상 암시적 기본값을 갖는 필드를 생성하는 유형 주석이 없습니다.
See [the migration guide](../migration.md#required-optional-and-nullable-fields) for more details on changes
to required and nullable fields.
해시할 수 없는 기본값이 있는 필드¶
Python에서 버그의 일반적인 원인은 변경 가능한 개체를 함수나 메서드 인수의 기본값으로 사용하는 것입니다. 동일한 인스턴스가 각 호출에서 재사용되기 때문입니다.
이 경우 dataclasses
모듈은 실제로 오류를 발생시켜 dataclasses.field
에 default_factory
인수를 사용해야 함을 나타냅니다.
Pydantic은 해시할 수 없는 기본값에 대해 default_factory
사용을 지원하지만 필수는 아닙니다. 기본값이 해시 가능하지 않은 경우 Pydantic은 모델의 각 인스턴스를 생성할 때 기본값을 심층 복사합니다.
from typing import Dict, List
from pydantic import BaseModel
class Model(BaseModel):
item_counts: List[Dict[str, int]] = [{}]
m1 = Model()
m1.item_counts[0]['a'] = 1
print(m1.item_counts)
#> [{'a': 1}]
m2 = Model()
print(m2.item_counts)
#> [{}]
동적 기본값이 있는 필드¶
기본값을 사용하여 필드를 선언할 때 해당 필드가 동적으로(즉, 모델마다 다름) 원할 수 있습니다. 이렇게 하려면 default_factory
를 사용할 수 있습니다.
예는 다음과 같습니다.
from datetime import datetime, timezone
from uuid import UUID, uuid4
from pydantic import BaseModel, Field
def datetime_now() -> datetime:
return datetime.now(timezone.utc)
class Model(BaseModel):
uid: UUID = Field(default_factory=uuid4)
updated: datetime = Field(default_factory=datetime_now)
m1 = Model()
m2 = Model()
assert m1.uid != m2.uid
Field
함수 문서에서 자세한 내용을 확인할 수 있습니다.
자동으로 제외된 속성¶
클래스 변수¶
typing.ClassVar
로 주석이 달린 속성은 Pydantic에서 클래스 변수로 적절하게 처리되며 모델 인스턴스의 필드가 되지 않습니다.
from typing import ClassVar
from pydantic import BaseModel
class Model(BaseModel):
x: int = 2
y: ClassVar[int] = 1
m = Model()
print(m)
#> x=2
print(Model.y)
#> 1
비공개 모델 속성¶
??? api "API 문서" pydantic.fields.PrivateAttr
이름 앞에 밑줄이 있는 속성은 Pydantic에서 필드로 처리되지 않으며 모델 스키마에 포함되지 않습니다. 대신, 이는 __init__
, model_validate
등을 호출하는 동안 유효성이 검사되지 않거나 심지어 설정되지 않는 "개인 속성"으로 변환됩니다.
!!! note Pydantic v2.1.0부터 비공개 속성과 함께 Field
함수를 사용하려고 하면 NameError가 발생합니다. 개인 속성은 필드로 처리되지 않으므로 Field() 함수를 적용할 수 없습니다.
다음은 사용 예입니다.
from datetime import datetime
from random import randint
from pydantic import BaseModel, PrivateAttr
class TimeAwareModel(BaseModel):
_processed_at: datetime = PrivateAttr(default_factory=datetime.now)
_secret_value: str
def __init__(self, **data):
super().__init__(**data)
# this could also be done with default_factory
self._secret_value = randint(1, 5)
m = TimeAwareModel()
print(m._processed_at)
#> 2032-01-02 03:04:05.000006
print(m._secret_value)
#> 3
모델 필드와의 충돌을 방지하려면 개인 속성 이름은 밑줄로 시작해야 합니다. 그러나 Dunder 이름(예: __attr__
)은 지원되지 않습니다.
데이터 변환¶
Pydantic은 입력 데이터를 캐스팅하여 모델 필드 유형을 따르도록 강제할 수 있으며, 어떤 경우에는 이로 인해 정보가 손실될 수 있습니다. 예를 들어:
from pydantic import BaseModel
class Model(BaseModel):
a: int
b: float
c: str
print(Model(a=3.000, b='2.72', c=b'binary data').model_dump())
#> {'a': 3, 'b': 2.72, 'c': 'binary data'}
이는 Pydantic의 의도적인 결정이며 가장 유용한 접근 방식인 경우가 많습니다. 주제에 대한 자세한 내용은 여기를 참조하세요.
그럼에도 불구하고 엄격한 유형 검사 도 지원됩니다.
모델 시그니처¶
모든 Pydantic 모델은 해당 필드를 기반으로 생성된 서명을 갖습니다.
import inspect
from pydantic import BaseModel, Field
class FooModel(BaseModel):
id: int
name: str = None
description: str = 'Foo'
apple: int = Field(alias='pear')
print(inspect.signature(FooModel))
#> (*, id: int, name: str = None, description: str = 'Foo', pear: int) -> None
정확한 서명은 자체 조사 목적과 FastAPI
또는 hypothesis
과 같은 라이브러리에 유용합니다.
생성된 서명은 사용자 정의 __init__
함수도 존중합니다.
import inspect
from pydantic import BaseModel
class MyModel(BaseModel):
id: int
info: str = 'Foo'
def __init__(self, id: int = 1, *, bar: str, **data) -> None:
"""My custom init!"""
super().__init__(id=id, bar=bar, **data)
print(inspect.signature(MyModel))
#> (id: int = 1, *, bar: str, info: str = 'Foo') -> None
서명에 포함되려면 필드의 별칭이나 이름이 유효한 Python 식별자여야 합니다. Pydantic은 서명을 생성할 때 필드의 별칭을 이름보다 우선시하지만 별칭이 유효한 Python 식별자가 아닌 경우 필드 이름을 사용할 수 있습니다.
필드의 별칭과 이름이 모두 유효한 식별자가 아닌 경우( create_model
의 이국적인 사용을 통해 가능할 수 있음) **data
인수가 추가됩니다. 또한 **data
인수는 다음과 같은 경우 서명에 항상 존재합니다. model_config['extra'] == 'allow'
.
구조적 패턴 일치¶
Pydantic은 Python 3.10의 PEP 636 에 도입된 대로 모델에 대한 구조적 패턴 일치를 지원합니다.
from pydantic import BaseModel
class Pet(BaseModel):
name: str
species: str
a = Pet(name='Bones', species='dog')
match a:
# match `species` to 'dog', declare and initialize `dog_name`
case Pet(species='dog', name=dog_name):
print(f'{dog_name} is a dog')
#> Bones is a dog
# default case
case _:
print('No dog matched')
!!! note 대/소문자 일치 문은 새 모델을 생성하는 것처럼 보일 수 있지만 속지 마십시오. 이는 속성을 가져오고 이를 비교하거나 선언하고 초기화하기 위한 구문 설탕일 뿐입니다.
속성 사본¶
대부분의 경우 생성자에 전달된 인수는 유효성 검사를 수행하고 필요한 경우 강제 변환을 수행하기 위해 복사됩니다.
이 예에서는 목록의 ID가 유효성 검사 중에 복사되었기 때문에 클래스가 생성된 후 변경된다는 점에 유의하세요.
from typing import List
from pydantic import BaseModel
class C1:
arr = []
def __init__(self, in_arr):
self.arr = in_arr
class C2(BaseModel):
arr: List[int]
arr_orig = [1, 9, 10, 3]
c1 = C1(arr_orig)
c2 = C2(arr=arr_orig)
print('id(c1.arr) == id(c2.arr):', id(c1.arr) == id(c2.arr))
#> id(c1.arr) == id(c2.arr): False
!!! note 모델을 전달할 때와 같이 Pydantic이 속성을 복사하지 않는 일부 상황이 있습니다. 우리는 모델을 있는 그대로 사용합니다. 설정하여 이 동작을 재정의할 수 있습니다. model_config['revalidate_instances'] = 'always'
.
추가 필드¶
기본적으로 Pydantic 모델은 인식할 수 없는 필드에 데이터를 제공하면 오류가 발생하지 않으며 무시됩니다.
from pydantic import BaseModel
class Model(BaseModel):
x: int
m = Model(x=1, y='a')
assert m.model_dump() == {'x': 1}
오류가 발생하도록 하려면 model_config
를 통해 이를 수행할 수 있습니다.
from pydantic import BaseModel, ConfigDict, ValidationError
class Model(BaseModel):
x: int
model_config = ConfigDict(extra='forbid')
try:
Model(x=1, y='a')
except ValidationError as exc:
print(exc)
"""
1 validation error for Model
y
Extra inputs are not permitted [type=extra_forbidden, input_value='a', input_type=str]
"""
대신 제공된 추가 데이터를 보존하려면 extra='allow'
설정할 수 있습니다. 그런 다음 추가 필드는 BaseModel.__pydantic_extra__
에 저장됩니다.
from pydantic import BaseModel, ConfigDict
class Model(BaseModel):
x: int
model_config = ConfigDict(extra='allow')
m = Model(x=1, y='a')
assert m.__pydantic_extra__ == {'y': 'a'}
기본적으로 이러한 추가 항목에는 유효성 검사가 적용되지 않지만 __pydantic_extra__
에 대한 유형 주석을 재정의하여 값에 대한 유형을 설정할 수 있습니다.
from typing import Dict
from pydantic import BaseModel, ConfigDict, Field, ValidationError
class Model(BaseModel):
__pydantic_extra__: Dict[str, int] = Field(init=False) # (1)!
x: int
model_config = ConfigDict(extra='allow')
try:
Model(x=1, y='a')
except ValidationError as exc:
print(exc)
"""
1 validation error for Model
y
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
"""
m = Model(x=1, y='2')
assert m.x == 1
assert m.y == 2
assert m.model_dump() == {'x': 1, 'y': 2}
assert m.__pydantic_extra__ == {'y': 2}
= Field(init=False)
는 런타임에 아무런 영향을 미치지 않지만 유형 검사기에서__pydantic_extra__
필드가 모델의__init__
메서드에 대한 인수로 처리되는 것을 방지합니다.
동일한 구성이 TypedDict
및 dataclass
'에 적용됩니다. 단, 구성은 클래스의 __pydantic_config__
속성을 유효한 ConfigDict
로 설정하여 제어됩니다.
本文总阅读量次