Python高级用法:装饰器用于参数检查

装饰器用于参数检查

检查函数接受或返回的参数,在特定上下文中执行时可能有用。XML-RPC协议是一种轻量级的远程过程调用(Remote Procedure Call)协议,通过HTTP使用XML对调用进行编码。自定义装饰器可以提供这种类型的签名,并确保输入和输出的类型,简单来讲就是创建一个用于检查输入参数和输出参数类型的装饰器

首先展示完整的代码

rpc_info = {}

def xmlrpc(in_=(), out=(type(None),)):
    # in_:输入数据的类型, out输出数据的类型
    def _xmlrpc(function):
        # 注册签名,本质上就是构建了一个字典,以函数名为key,输入参数和输出参数的类型为value
        func_name = function.__name__
        rpc_info[func_name] = (in_, out)
        def _check_types(elements, types):
            """用来检查类型的子函数。"""
            if len(elements) != len(types):
                # 检查参数数量与要验证的参数数量
                raise TypeError('argument count is wrong')
            typed = enumerate(zip(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))


        # 包装过的函数
        def __xmlrpc(*args):  # 没有允许的关键词
            # 检查输入的内容
            checkable_args = args[1:]  # 去掉self
            _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

rpc_info是一个字典,用于修饰器中的注册功能(传入的函数名与输入输出类型的键值对)。

在xmlrpc中,in_传入的是输入参数的类型,out传入的是输出参数的类型。

在_xmlrpc首先完成传入的函数名与输入输出类型的键值对,在_xmlrpc中构建了_check_types和__xmlrpc函数,其中_check_types函数用于检查参数的类型,__xmlrpc函数先调用_check_types检查输入参数的类型,随后调用function获取输出结果。随后再调用_check_types检查输出类型。

_check_types输入参数有elements和types,elements对应的是函数的参数,types对应的是参数应当对应的类型。首先要判断elements和types的数量是否一致,当数量一致时逐个判断参数是否满足要求的类型(isinstance用来判断类型是否相同)。

在__xmlrpc中,我们着重来看下面这一段代码

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

这段代码是用来判断res是否为元组或者列表,因为我们在_check_types采用的是逐个遍历可迭代对象(元组或者列表)的方式,但是function的返回值不一定是可迭代的对象,所以这里我们需要强行把他变为可迭代的对象。

下面我们使用一个例子来展现效果

class RPCView:
    @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

在RPCView类中我们构建了两个类方法,meth1和meth2,用于判断他们传入参数和输出参数的类型

如果我们打印一下rpc_info,可以看到注册后的信息

print(rpc_info)

运行结果为:

{'meth1': ((<class 'int'>, <class 'int'>), (<class 'NoneType'>,)), 'meth2': ((<class 'str'>,), (<class 'int'>,))}

即函数名与传入和传出参数的键值对
我们可以将类进行实例化并测试

my = RPCView()
my.meth1(1, 2)
my.meth2(2)

在调用meth1时会输出received 1 and 2,但是调用meth2时由于我们输入的并不是字符串,所以经修饰器检查不符合,会报错。

你可能感兴趣的:(python高级用法,python,开发语言)