python装饰器和函数注解验证参数有效范围

发布时间:2024年01月17日

1 python装饰器和函数注解验证参数有效范围

python可以通过注解来存储参数的有效范围,给到装饰器进行校验。

1.1 函数注解

python把注解保存到字典并且赋值给函数的__annotations__属性。参考《python函数属性和注解

描述

每个函数入参的注解只允许编写1个表达式,即不能同时给入参默认值和指定范围。

示例

>>> def annof(a,b:(2,5),c:'c'=1):
    return a+b+c

>>> annof(1,6)
8
>>> annof.__annotations__
{'b': (2, 5), 'c': 'c'}

1.2 函数注解验证参数有效范围

描述

将函数入参的有效范围通过注解保存到字典,装饰器再对入参实际值判断是否在有效范围。

(1) 装饰器获取装饰函数或方法的注解argchecks**=func.**__annotations__;

(2) 装饰函数或方法编写注解,mixargs**(a😦1,10),b😦2,20),c😦3,30),d😦5,50))**给到函数注解属性;

(3) 装饰器将获得的入参实际值,与注解比较,判断是否在有效范围。

示例

>>> import inspect
>>> def get_default_args(func):
    #获取入参默认值
    signature = inspect.signature(func)
    return {
        k: v.default
        for k, v in signature.parameters.items()
        if v.default is not inspect.Parameter.empty
    }
>>> def rangetest(func):#接收函数或方法
    if not __debug__:#内置变量,默认True
        return func
    else:
        defaultargs=get_default_args(func)
        code=func.__code__
        allargs=code.co_varnames[:code.co_argcount]
        funcname=func.__name__
        def onCall(*pargs,**kargs):#函数或方法入参,支持位置和关键字传参
            positionals=list(allargs)
            positionals=positionals[:len(pargs)]
            # func.__annotations__ 获取函数注解内容
            argchecks=func.__annotations__
            print('argchecks='+str(argchecks))
            
            for (argname,(low,high)) in argchecks.items():
                # 遍历argchecks验证入参有效范围
                if argname in kargs:#关键字传参验证
                    if kargs[argname]<low or kargs[argname]>high:
                        errmsg='{}入参"{}"的值 {} 不在[{},{}]范围内'
                        errmsg=errmsg.format(funcname,argname,kargs[argname],low,high)
                        raise ValueError(errmsg)
                elif argname in positionals:#位置传参验证
                    position=positionals.index(argname)
                    if pargs[position]<low or pargs[position]>high:
                        errmsg='{}入参"{}"的值 {} 不在[{},{}]范围内'
                        errmsg=errmsg.format(funcname,argname,pargs[position],low,high)
                        raise ValueError(errmsg)
                else:#参数默认值不验证
                    msg='{}入参 "{}"取默认值 {}'
                    print(msg.format(funcname,argname,defaultargs[argname]))
            return func(*pargs,**kargs)
        return onCall
>>> def exDecorator(func):
    def onCall(*args,**kargs):
        try:
            return func(*args,**kargs)
        except ValueError as ve:
            print(ve)
    return onCall

>>> @exDecorator
@rangetest
def mixargs(a:(1,10),b:(2,20),c:(3,30),d:(5,50)):
    print(a,b,c,d)

    
>>> mixargs(1,2,3,5)
argchecks={'a': (1, 10), 'b': (2, 20), 'c': (3, 30), 'd': (5, 50)}
1 2 3 5
>>> mixargs(2,d=9,c=18,b=5)
argchecks={'a': (1, 10), 'b': (2, 20), 'c': (3, 30), 'd': (5, 50)}
2 5 18 9
>>> mixargs(2,d=9,c=38,b=5)
argchecks={'a': (1, 10), 'b': (2, 20), 'c': (3, 30), 'd': (5, 50)}
mixargs入参"c"的值 38 不在[3,30]范围内
>>> mixargs(a=11,d=9,c=8,b=5)
argchecks={'a': (1, 10), 'b': (2, 20), 'c': (3, 30), 'd': (5, 50)}
mixargs入参"a"的值 11 不在[1,10]范围内
>>> mixargs(1)
argchecks={'a': (1, 10), 'b': (2, 20), 'c': (3, 30), 'd': (5, 50)}
Traceback (most recent call last):
  File "<pyshell#62>", line 1, in <module>
    mixargs(1)
  File "<pyshell#57>", line 4, in onCall
    return func(*args,**kargs)
  File "<pyshell#55>", line 31, in onCall
    print(msg.format(funcname,argname,defaultargs[argname]))
KeyError: 'b'
文章来源:https://blog.csdn.net/sinat_34735632/article/details/135637264
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。