python可以通过注解来存储参数的有效范围,给到装饰器进行校验。
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) 装饰器获取装饰函数或方法的注解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'