attrs-validators
バリデーションライブラリ
attrs-validators
概要
attrs-validatorsは、Pythonのattrsライブラリに統合されたバリデーション機能です。データクラスの定義と同時にバリデーションロジックを埋め込むことができ、型安全で堅牢なPythonコードの作成を支援します。attrsのデータクラスに対して、組み込みバリデータやカスタムバリデータを使用して、実行時の値検証を提供します。
詳細
attrs-validatorsはattrsライブラリの一部として提供される強力なバリデーション機能です。attrsは、Pythonのデータクラスを作成するための先進的なライブラリで、標準のdataclassesよりも多くの機能を提供します。バリデーション機能はattr.validatorsモジュールを通じて利用でき、型チェック、値の範囲検証、カスタムバリデーションロジックなど、さまざまな検証パターンをサポートしています。
主な特徴として、宣言的なバリデーション定義、コンバータとの組み合わせ、複数バリデータのチェイン、属性間の相互検証などがあります。また、バリデーションは初期化時だけでなく、属性の設定時にも実行できるため、オブジェクトの整合性を常に保証できます。
メリット・デメリット
メリット
- 宣言的な構文: クラス定義と同時にバリデーションルールを明確に定義できる
- 豊富な組み込みバリデータ: instance_of、in_、ge/gt/le/lt、deep_iterableなど、よく使うバリデータが組み込まれている
- カスタムバリデータの簡単な作成: デコレータを使用して直感的にカスタムバリデータを定義可能
- コンバータとの統合: 値の変換後にバリデーションを実行できるため、柔軟な入力処理が可能
- 属性間の相互検証: 複数の属性値を参照する複雑なバリデーションロジックも実装可能
- パフォーマンス: C拡張による高速な実行と、バリデーションの無効化オプション
デメリット
- attrsライブラリへの依存: attrsライブラリ全体を使用する必要があり、既存のプロジェクトへの導入が難しい場合がある
- 学習曲線: attrsの独自構文に慣れる必要がある
- 標準dataclassesとの非互換: Pythonの標準dataclassesとは異なるアプローチのため、移行が困難
- デコレータの多用: 複雑なバリデーションでは多くのデコレータが必要になり、コードが読みにくくなる可能性
参考ページ
書き方の例
基本的な使い方
import attr
from attr import validators
@attr.s
class User:
# 型検証
name = attr.ib(validator=validators.instance_of(str))
# 範囲検証
age = attr.ib(validator=[
validators.instance_of(int),
validators.ge(0), # 0以上
validators.le(150) # 150以下
])
# 選択肢検証
status = attr.ib(validator=validators.in_(["active", "inactive"]))
# 使用例
user = User(name="田中", age=25, status="active")
# user = User(name="田中", age=-1, status="active") # ValueError
カスタムバリデータ
from attrs import define, field
@define
class Product:
name: str = field()
price: float = field()
quantity: int = field()
@price.validator
def validate_price(self, attribute, value):
if value <= 0:
raise ValueError("価格は0より大きい必要があります")
if value > 1000000:
raise ValueError("価格は100万円以下にしてください")
@quantity.validator
def validate_quantity(self, attribute, value):
if value < 0:
raise ValueError("数量は負の値にできません")
# 使用例
product = Product(name="ノートPC", price=89800, quantity=10)
コンバータとバリデータの組み合わせ
import attr
from attr import validators
def to_upper(value):
"""文字列を大文字に変換"""
return value.upper() if isinstance(value, str) else value
@attr.s
class Code:
# 変換してからバリデーション
country_code = attr.ib(
converter=to_upper,
validator=[
validators.instance_of(str),
validators.in_(["JP", "US", "UK", "CN"])
]
)
postal_code = attr.ib(
converter=str, # 数値も文字列に変換
validator=validators.matches_re(r"^\d{3}-\d{4}$")
)
# 使用例
code = Code(country_code="jp", postal_code="123-4567") # "jp"は"JP"に変換される
print(code.country_code) # "JP"
属性間の相互検証
from attrs import define, field
def end_after_start(instance, attribute, value):
"""終了日が開始日より後であることを検証"""
if instance.start_date and value <= instance.start_date:
raise ValueError("終了日は開始日より後である必要があります")
@define
class DateRange:
start_date: str = field()
end_date: str = field(validator=end_after_start)
@start_date.validator
def validate_start_date(self, attribute, value):
# 日付形式の検証
try:
from datetime import datetime
datetime.strptime(value, "%Y-%m-%d")
except ValueError:
raise ValueError("日付はYYYY-MM-DD形式で入力してください")
# 使用例
date_range = DateRange(start_date="2024-01-01", end_date="2024-12-31")
論理演算子を使った複合バリデーション
from attrs import define, field, validators
@define
class FlexibleInput:
# 文字列または整数を受け入れる
value = field(validator=validators.or_(
validators.instance_of(str),
validators.instance_of(int)
))
# オプショナルなメールアドレス
email = field(
default=None,
validator=validators.optional(
validators.matches_re(r"^[\w\.-]+@[\w\.-]+\.\w+$")
)
)
# 使用例
input1 = FlexibleInput(value="text")
input2 = FlexibleInput(value=123, email="[email protected]")
input3 = FlexibleInput(value=123) # emailはNone(オプショナル)
バリデーションの動的な制御
import attr
from attr import validators
# バリデーションを一時的に無効化
with validators.disabled():
@attr.s
class TempData:
value = attr.ib(validator=validators.instance_of(int))
# バリデーションが無効なので、文字列でもエラーにならない
temp = TempData(value="not an int")
# コンテキスト外ではバリデーションが有効
# temp2 = TempData(value="not an int") # これはエラーになる