LoginSignup
0
0

More than 1 year has passed since last update.

pydanticによる型検証 [BaseModel]

Last updated at Posted at 2022-10-02

はじめに

Pythonは動的な型付けであるため,変数の型が保証されていない。型ヒントによる宣言やmypyによるチェックは可能だが実行時の制限はない。
そこで,pydanticによる型に堅牢な書き方を記録する。

TL;DR

  • pydanticを使用すると簡単に型検証が行える
    • pydantic.BaseModelの継承と型ヒント
  • 型の宣言はtypingなどによる型ヒントで行う

pydanticとは

下記は公式ドキュメントによる説明です。

  • Data validation and settings management using Python type annotations.
  • pydantic enforces type hints at runtime, and provides user friendly errors when data is invalid.
  • Define how data should be in pure, canonical Python; validate it with pydantic.
  • google 翻訳

    • Pythonの型アノテーションを使ったデータ検証・設定管理
    • pydanticは実行時に型ヒントを強制し,データが無効な場合はユーザーフレンドリーなエラーを提供
    • データがどのようにあるべきかを純粋で標準的なPythonで定義,検証

インストール

pydanticはpythonの標準モジュールではないので下記コマンドによるインストールが必要
pip install pydantic

使用方法

バージョンは以下のとおり

  • Python: 3.10.5
  • Pydantic: 1.10.2

以下サンプルコードはimport pydanticされている前提

基本 pydantic.BaseModel

pydanticのBaseModelを継承して使用する。また,型宣言は型ヒントにより記述する。
型宣言に対して不整合な値が入力された場合はエラーが発生する。しかし,宣言と異なる型の値でもキャストが可能な場合はキャストして代入される。厳密な型検証を行いたい場合は次項を参照

model.py
class User(pydantic.BaseModel):
    id_: int
    name: str


print(f'{User(id_=100, name="abc")   = }')
print(f'{User(id_=100.0, name="abc") = }')
print(f'{User(id_="100", name="abc") = }')
try:
    User(id_="abc", name="abc")
except pydantic.ValidationError as e:
    print("User(id_='abc', name='abc') = ")
    print(f"{type(e).__name__}: {e}")

# User(id_=100, name="abc")   = User(id_=100, name='abc')
# User(id_=100.0, name="abc") = User(id_=100, name='abc')
# User(id_="100", name="abc") = User(id_=100, name='abc')
# User(id_='abc', name='abc') =
# ValidationError: 1 validation error for User
# id_
#   value is not a valid integer (type=type_error.integer)

boolを宣言した場合は以下のようにキャストされる。

  • False: False, 0, '0', 'off', 'f', 'false', 'n', 'no'
  • True: True, 1, '1', 'on', 't', 'true', 'y', 'yes'
model.py
class BooleanModel(pydantic.BaseModel):
    is_enabled: bool


print(f'{BooleanModel(is_enabled="on")  = }')
print(f'{BooleanModel(is_enabled="off") = }')
try:
    BooleanModel(is_enabled=-1)
except pydantic.ValidationError as e:
    print("BooleanModel(is_enabled=-1) = ")
    print(f"{type(e).__name__}: {e}")

# BooleanModel(is_enabled="on")  = BooleanModel(is_enabled=True)
# BooleanModel(is_enabled="off") = BooleanModel(is_enabled=False)
# BooleanModel(is_enabled=-1) =
# ValidationError: 1 validation error for BooleanModel
# is_enabled
#   value could not be parsed to a boolean (type=type_error.bool)

初期値と必須項目

初期値を設定することが可能であり,その場合は必須項目ではなくなる。初期値を設定する場合は型宣言を行わなくとも初期値から型が推定される。

model.py
class UserWithNumber(pydantic.BaseModel):
    id_: int
    name: str = "abc"
    number = 0


print(f"{UserWithNumber(id_=100) = }")
try:
    UserWithNumber()
except pydantic.ValidationError as e:
    print("UserWithNumber() = ")
    print(f"{type(e).__name__}: {e}")

try:
    UserWithNumber(id_=100, number="abc")
except pydantic.ValidationError as e:
    print("UserWithNumber(id_=100, number='abc') = ")
    print(f"{type(e).__name__}: {e}")


# UserWithNumber(id_=100) = UserWithNumber(id_=100, name='abc', number=0)
# UserWithNumber() =
# ValidationError: 1 validation error for UserWithNumber
# id_
#   field required (type=value_error.missing)
# UserWithNumber(id_=100, number='abc') =
# ValidationError: 1 validation error for UserWithNumber
# number
#   value is not a valid integer (type=type_error.integer)

複数の型宣言とNull許容

型宣言には複数の型を宣言することが可能。その場合は左の型から優先される。
Noneを合わせて宣言することでNullを許容することができる。(新しいバージョンでは初期値とNull許容は別になる予定)

model.py
class NullableModel(pydantic.BaseModel):
    id_: int | None
    name: str


print(f'{NullableModel(id_=100, name="abc")  = }')
print(f'{NullableModel(id_=None, name="abc") = }')
print(f'{NullableModel(name="abc")           = }')
try:
    NullableModel(name=None)
except pydantic.ValidationError as e:
    print("NullableModel(name=None) = ")
    print(f"{type(e).__name__}: {e}")

# NullableModel(id_=100, name="abc")  = NullableModel(id_=100, name='abc')
# NullableModel(id_=None, name="abc") = NullableModel(id_=None, name='abc')
# NullableModel(name="abc")           = NullableModel(id_=None, name='abc')
# NullableModel(name=None) =
# ValidationError: 1 validation error for NullableModel
# name
#   none is not an allowed value (type=type_error.none.not_allowed)

厳密な型検証

型検証では宣言された型にキャストされる。そのため,int型にfloat値を入力すると小数点以下は切り捨てられる。
厳密に型を検証したい場合はpydantic.StrictIntpydantic.StrictBoolなどの厳密な型を指定する。(新しいバージョンでは Strict Mode が導入される予定)

model.py
class StrictIntModel(pydantic.BaseModel):
    strict_int: pydantic.StrictInt


try:
    StrictIntModel(strict_int=3.14159)
except pydantic.ValidationError as e:
    print("StrictIntModel(strict_int=3.14159) = ")
    print(f"{type(e).__name__}: {e}")

# StrictIntModel(strict_int=3.14159) =
# ValidationError: 1 validation error for StrictIntModel
# strict_int
#   value is not a valid integer (type=type_error.integer)


class StrictBoolModel(pydantic.BaseModel):
    strict_bool: pydantic.StrictBool


try:
    StrictBoolModel(strict_bool="False")
except pydantic.ValidationError as e:
    print("StrictBoolModel(strict_bool='False') = ")
    print(f"{type(e).__name__}: {e}")

# StrictBoolModel(strict_bool='False') =
# ValidationError: 1 validation error for StrictBoolModel
# strict_bool
#   value is not a valid boolean (type=value_error.strictbool)

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0