関数の引数に渡された値の型をチェックするデコレータを書いてみた

C# 暦の長い私が動的言語Python に初めて触れたとき、関数の引数にいろんな型の値を渡せることを気持ち悪く感じました。今では慣れましたけど。


Python動的言語なので、正常に動くかどうかは別として、関数の引数に何でも渡せます。その値が使えるかどうかを判断するのは、関数内部でのお仕事。サポートしている型の値かどうか判断する if 文の何回も書くのは正直面倒です。


そこで、引数の型をチェックするデコレータを作ってみました。Python の標準ライブラリで提供されている inspect モジュールを使って、引数名や渡された値といった、関数の情報を取得しています。

def require_type(name, klass):
    """引数の型をチェックするデコレータ"""
    def _require_type(func):
        def __require_type(*args, **kwargs):
            import inspect
            # 関数の情報を取得
            spec = inspect.getargspec(func)
            # 指定した名前の引数が存在するとき型をチェック
            if name in spec.args:
                index = spec.args.index(name)
                if not isinstance(args[index], klass):
                    raise TypeError("%s is not %s." % (name, klass.__name__)) 
            return func(*args, **kwargs)
        return __require_type
    return _require_type

@require_type("name", str) # name は文字列
@require_type("age", int)  # age は整数
def greet(name, age):
    print("%s, %s" % (name, age))

greet("Hayate", 16) #=> Hayate, 16
try:
    greet("Hinagiku", "16")
except TypeError, ex:
    print(ex) #=> age is not int.

これを応用すれば、引数の null チェックや範囲チェックもデコレータで書けます。いっそのこと、ラムダ式を渡せるようにしてもいいですね。inspect モジュールはなかなか興味深いです。