Pydantic
Pydantic은 파이썬의 Type Hint
를 사용하여 데이터의 유효성을 검사하는 라이브러리이다.
파이썬에서 Type Hint
는 변수 타입에 대한 힌트일 뿐, 다른 타입을 명시한다고 해서 문제가 발생하지는 않는다.
이러한 문제 때문에 pydantic
은 파이썬에게 런타임에서 Type hint를 강제하여, 타입이 유효하지 않으면 error를 발생시킨다.
Pydantic은 기본적으로 유효성 검사를 위해 사용되지만, 엄밀하게 따지면 parsing 라이브러리이다.
출력 모델의 자료형이나 제약 조건을 보장하기 위한 수단으로 입력 데이터의 유효성을 검사한다.
결론적으로는 주로 사용되는 API의 I/O
뿐만 아니라 다양한 함수를 안정적으로 사용하기 위한 도구로 볼 수 있겠다.
설치
pydantic
은 파이썬 3.7
버전 이상이라면 문제없이 사용 가능하다.
pip install pydantic
예시
간단한 예제를 통해서 pydantic의 역할을 이해해보자.
from datetime import datetime
from pydantic import BaseModel
class Model(BaseModel):
a: int
b: float
c: str
d: bool
명시된 자료형에 맞는 데이터가 들어온다면, 코드는 정상 동작한다.
external_data :dict[str,int|float|str|bool] = {
"a": 10,
"b": 10.5,
"c": "Hello",
"d": False
}
result: dict[str,int|float|str|bool] = Model(**external_data).model_dump()
print(result)
# 출력
{'a': 10, 'b': 10.5, 'c': 'Hello', 'd': False}
근데 여기서 다른 자료형이 들어오면 에러를 발생시키 사용자가 확인하기 쉽도록 명시해준다.
external_data :dict[str,int|float|str|bool] = {
"a": "Hello",
"b": 10.5,
"c": "Hello",
"d": False
}
result: dict[str,int|float|str|bool] = Model(**external_data).model_dump()
print(result)
# 출력
Traceback (most recent call last):
File "/home/a-user/github/ORM/00_pydantic연습/main.py", line 18, in <module>
result = Model(**external_data).model_dump()
^^^^^^^^^^^^^^^^^^^^^^
File "/home/a-user/.pyenv/versions/orm/lib/python3.11/site-packages/pydantic/main.py", line 193, in __init__
self.__pydantic_validator__.validate_python(data, self_instance=self)
pydantic_core._pydantic_core.ValidationError: 1 validation error for Model
a
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='Hello', input_type=str]
For further information visit https://errors.pydantic.dev/2.8/v/int_parsing
만약 예외적으로 처리가 가능한 값이라면 자동으로 명시된 자료형에 맞춰주어 코드의 안정성을 높여준다.
external_data :dict[str,int|float|str|bool] = {
"a": "10",
"b": 10.5,
"c": "Hello",
"d": False
}
result: dict[str,int|float|str|bool] = Model(**external_data).model_dump()
print(result)
# 출력
{'a': 10, 'b': 10.5, 'c': 'Hello', 'd': False}
심화
Making Models
Models는 BaseModel
로부터 상속받은 Class로, parsing과 validation을 통해 정의된 필드와 제약을 보장해준다.
앞선 예시를 통해서 유효성 검사를 통해 자료형을 확인해준다는 것은 확인해봤으니
추가적으로 또 어떤 것들이 가능한지 다른 예시를 들어서 이해해보자.
from pydantic import BaseModel
DATA_TYPE = dict[str, str|int|list[str]|None]
class Person(BaseModel):
first_name: str
middle_name: str | None
last_name: str
age: int
interest: list[str] | None = []
data :DATA_TYPE = {
"first_name": "Jaehyo",
"last_name": "Lee",
"age": 27
}
result = Person(**data).model_dump()
print(result)
여기서 Person
이라는 Model Class에 정의된 필드들을 자세하게 살펴봐보자.
first_name
str
타입의 변수를 의미. 그리고 반드시 필요한 필드
middle_name
str
타입의 변수를 의미. 반드시 필요하지 않은 필드, 설정하지 않으면None
으로 지정됨
last_name
str
타입의 변수를 의미. 그리고 반드시 필요한 필드
age
int
타입의 변수를 의미. 그리고 반드시 필요한 필드
interest
str
타입의list
를 의미. 반드시 필요하지 않은 필드, 설정하지 않으면[]
으로 지정됨
Custom validation
validator decorator
를 사용하여 자체적인 규칙을 만들어 유효성 검사를 진행할 수도 있다.
from pydantic import BaseModel, field_validator, ValidationError
class Person(BaseModel):
name: str
age: int
@field_validator('name')
def name_must_english(cls, v: str) -> str:
assert v.encode().isalpha(), ValueError("Must me english")
return v
@field_validator('age')
def adult_check(cls, v: int) -> int:
if v <= 19:
raise ValueError("not an adult")
return v
data = {
"name": "이재효",
"age": 27
}
try:
result = Person(**data).model_dump()
print(result)
except ValidationError as e:
print(e)
# 출력
1 validation error for Person
name
Assertion failed, Must me english [type=assertion_error, input_value='이재효', input_type=str]
For further information visit https://errors.pydantic.dev/2.8/v/assertion_error
Making JSON from Model
Model로부터 JSON을 생성하는 것도 가능하다.
from pydantic import BaseModel
class Person(BaseModel):
first_name: str
last_name: str
age: int
data = {
"first_name": "Jaehyo",
"last_name": "Lee",
"age": 27
}
result = Person(**data).schema_json(indent=2)
print(result)
# 출력
{
"properties": {
"first_name": {
"title": "First Name",
"type": "string"
},
"last_name": {
"title": "Last Name",
"type": "string"
},
"age": {
"title": "Age",
"type": "integer"
}
},
"required": [
"first_name",
"last_name",
"age"
],
"title": "Person",
"type": "object"
}