Python
python3

デコレータを使ってバリデーションチェックしてみた

Pythonでのバリデーションチェックについて考えてみた。
結論だけ言うと、CerberusやSchematicsのライブラリを使おうってところなんだけど、自分でも書いてみたので残しておく。

簡易的には、以下のように書くとチェックできる。

class User:

    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.__check_validate()

    def __check_validate(self):

        if self.name:
            print('validation check:OK')
        else:
            print('validation check:NG')

        if 0 <= self.age <= 100:
            print('validation check:OK')
        else:
            print('validation check:NG')

ただし、これだと毎回チェック内容をコーディングしなきゃいけなくなる。
なので、デコレータを使って簡単に書けるようにしてみた。

from functools import wraps


def required(func):

    @wraps(func)
    def __wrapper(*args, **kwargs):
        if args[1]:
            print('validation check:OK')
        else:
            print('validation check:NG')
        func(*args, **kwargs)

    return __wrapper


def int_range(minimum, maximum):
    def __int_range(func):

        @wraps(func)
        def __wrapper(*args, **kwargs):
            if minimum <= args[1] <= maximum:
                print('validation check:OK')
            else:
                print('validation check:NG')
            func(*args, **kwargs)

        return __wrapper
    return __int_range


class User:

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return self.name + ':' + str(self.age)

    @property
    def name(self):
        return self.__name

    @name.setter
    @required
    def name(self, name):
        self.__name = name

    @property
    def age(self):
        return self.__age

    @age.setter
    @int_range(0, 100)
    def age(self, age):
        self.__age = age


user1 = User('Sato', 33)
print(user1)

user2 = User('', 34)
print(user2)

user3 = User('Suzuki', 101)
print(user3)

バリデーションチェックするデコレータを作成して、setterにつける。

デコレータをつけるだけなので何のチェックをしてるかわかりやすくなったけど、そのためにgetterとsetterを書かなきゃいけなくなるのがめんどう。
メソッドだけじゃなく、変数定義にもデコレータつけられるといいのにな。

ちなみにPyCharmだと、setter部分でインスタンス変数の定義は__init__でやるべきだ、と言われてしまいます。
でも、__init__の代入でもsetterを呼ぶ必要があるので、例外でsetterは許してほしい。