by shigemk2

当面は技術的なことしか書かない

エキスパートPythonプログラミング デコレータ 引数チェック

グローバルな辞書に関数を登録し、その引数と返り値の型のリストを保持する。
XML-RPC(軽量なRPC(Remote Procedure Call)プロトコル、SOAPの代わりに利用される)

デコレータを使用すると引数の型情報を設定できるため、入出力に渡される値がそれに合うことを
保証してくれるようになる。

from itertools import izip
rpc_info = {}
def xmlrpc(in_=(), out=(type(None) ,)):
    def _xmlrpc(function):
        # registration of parameters' information
        func_name = function.func_name
        rpc_info[func_name] = (in_, out)

        def _check_types(elements, types):
            """Subfunction that checks the types."""
            if len(elements) != len(types):
                raise TypeError('argument count is wrong')
            typed = enumerate(izip(elements, types))
            for index, couple in typed:
                arg, of_the_right_type = couple
                if isinstance(arg, of_the_right_type):
                    continue
                raise TypeError('arg #%d should be %s' %
                                (index, of_the_right_type))

        # wrapping function
        def __xmlrpc(*args):
            checkable_args = args[1:]
            _check_types(checkable_args, in_)

            res = function(*args)

            if not type(res) in (tuple, list):
                checkable_res = (res,)
            else:
                checkable_res = res
            _check_types(checkable_res, out)

            return res
        return __xmlrpc
    return _xmlrpc

class RPCView(object):

    @xmlrpc((int, int)) # two int -> None
    def meth1(self, int1, int2):
        print('received %d and %d' % (int1, int2))

    @xmlrpc((str,), (int,)) # string -> int
    def meth2(self, phrase):
        print('received %s' % phrase)
        return 12

print rpc_info
my = RPCView()
my.meth1(1,2)
my.meth2('1')
my.meth2(1)

{'meth2': *1, 'meth1': *2}
received 1 and 2
received 1
Traceback (most recent call last):
TypeError: arg #0 should be

デコレータを使用すると引数の型情報を設定できる

*1:,), (,

*2:, ), (,