python3の関数アノテーションを試してみました

ネタ的には今更なんですけど、よくわかっていなかったので試してみました。
予め言っておきますと初歩的な内容なので期待しないで下さい。

引数の情報を取得する為に
http://docs.python.org/3.2/library/inspect.html#inspect.getfullargspec

http://code.google.com/p/anntools/
を参考にさせて頂きました。

以下のようなtest.pyを用意してみます。

# -*- coding: utf-8 -*-
from functools import wraps
from inspect import getfullargspec


class ArgumentError(Exception):
    def __init__(self, arg_name, val, typ):
        super(ArgumentError, self).__init__()
        print('引数 {} の {} は {} と型が違います'.format(arg_name, val, typ))


class RetValError(Exception):
    def __init__(self, retval, typ):
        super(RetValError, self).__init__()
        print('{} は 戻り値の型が {} ではありません'.format(retval, typ))


def validate(func):
    @wraps(func)
    def _validate(*args, **kwargs):
        argSpec = getfullargspec(func)
        for name, value in zip(argSpec.args, args):
            obj_type = func.__annotations__[name]
            if not isinstance(value, obj_type):
                raise ArgumentError(name, value, obj_type)
        result = func(*args, **kwargs)
        ret_type = func.__annotations__['return']
        if not isinstance(result, ret_type):
            raise RetValError(result, ret_type)
        print('OK')
        return result
    return _validate


@validate
def hoge(a: int, b: str = '!') -> str:
    val = str(a) + b
    return val

実際にどうなるのか試してみます。

>>> from test import hoge
>>> hoge(1)
OK
'1!'
>>> hoge('1')
引数 a の 1 は <class 'int'> と型が違います
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "test.py", line 26, in _validate
    raise ArgumentError(name, value, obj_type)
test.ArgumentError
>>> hoge(1, 'a')
OK
'1a'
>>> hoge(1, 3)
引数 b の 3 は <class 'str'> と型が違います
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "test.py", line 26, in _validate
    raise ArgumentError(name, value, obj_type)
test.ArgumentError

実装次第で色々出来そうです。

今のところ僕の中では型判定以外の利用方法があまり浮かばないのですが
今後色々と出てくると思いますし楽しみです。