python验证位置参数、关键字参数、默认参数的有效范围。
python可以查看函数的名称、函数变量、入参个数等属性。参考《python函数属性和注解》
NO | 属性 | 描述 |
---|---|---|
1 | func.__name__ | 函数名 |
2 | func.__code__ | 函数代码对象 |
3 | func.__code__.co_varnames | 函数全部变量 |
4 | func.__code__.co_argcount | 函数入参个数 |
描述
将入参及有效范围保存在字典,对实际接收到的入参值进行有效范围验证。
(1) 最外层定义rangetest,入参argchecks将装饰函数或方法的入参和有效范围保存为字典,即保存需验证的入参有效范围;
(2) 次外层定义onDecorator,入参func保存装饰函数或方法;
(3) 最里层定义onCall,入参接收装饰函数或方法的位置和关键字入参;
(4) co_varnames[:code.co_argcount]获取装饰函数或方法定义的全部入参;
(5) pargs保存装饰函数或方法的实际位置入参值;
(6) kargs保存装饰函数或方法的实际关键字入参名和值;
(7) 需验证有效范围的入参名在实际关键字入参内,则进行校验;
(8) 需要在有效范围的入参名在实际位置入参内,则进行校验;
(9) 剩下的为默认入参,不校验;
示例
>>> 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(**argchecks):
#argchecks 参数字段,入参名为键,有效范围为值,传入需要验证的参数和范围
def onDecorator(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)]
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
return onDecorator
>>> def exDecorator(func):
def onCall(*args,**kargs):
try:
return func(*args,**kargs)
except ValueError as ve:
print(ve)
return onCall
描述
通过装饰器入参的k指定验证入参的名字,v指定验证入参的有效范围。
@rangetest(age=(0,120))的age表示persinfo(name,age)函数要验证的入参为age入参,0,120表示,age的有效范围为[0,120]。
示例
>>> @exDecorator
@rangetest(age=(0,120))#1 表示验证persinfo索引为1的入参age的范围
def persinfo(name,age):
print('%s 今年 %s 岁' %(name,age))
>>> @exDecorator
@rangetest(Y=(0,2999),M=(1,12),D=(1,31))#对Y,M,D都验证
def birthday(Y,M,D):
print('生日={0}年{1}月{2}日'.format(Y,M,D))
>>> persinfo('梯阅线条',36)
梯阅线条 今年 36 岁
>>> persinfo('梯阅线条',121)
persinfo入参"age"的值 121 不在[0,120]范围内
>>> birthday(2023,10,21)
生日=2023年10月21日
>>> birthday(3023,10,21)
birthday入参"Y"的值 3023 不在[0,2999]范围内
>>> birthday(2023,13,21)
birthday入参"M"的值 13 不在[1,12]范围内
>>> birthday(2023,12,51)
birthday入参"D"的值 51 不在[1,31]范围内
描述
通过装饰器入参的k指定验证入参的名字,v指定验证入参的有效范围。
@rangetest(percent=(0.0,1.0))的percent表示giveRaise(self,percent)方法要验证的入参为percent入参,0.0,1.0表示,percent的有效范围为[0.0,1.0]。
示例
>>> class Person:
def __init__(self,name,job,pay):
self.job=job
self.pay=pay
@exDecorator
@rangetest(percent=(0.0,1.0))
def giveRaise(self,percent):
self.pay=int(self.pay*(1+percent))
>>> p1=Person('梯阅线条','自由职业',500000)
>>> p1.giveRaise(0.2)
>>> p1.pay
600000
>>> p1.giveRaise(1.2)
giveRaise入参"percent"的值 1.2 不在[0.0,1.0]范围内
描述
@rangetest*(a=(1,10),b=(2,20),c=(3,30),d=(5,50))的a,b,c,d表示要验证的入参和对应的有效范围。支持位置传参、关键字传参及默认传参的有效范围校验。
示例
>>> @exDecorator
@rangetest(a=(1,10),b=(2,20),c=(3,30),d=(5,50))
def mixargs(a,b=7,c=8,d=9):
print(a,b,c,d)
>>> mixargs(1,2,3,5)
1 2 3 5
>>> mixargs(1)
mixargs入参 "b"取默认值 7
mixargs入参 "c"取默认值 8
mixargs入参 "d"取默认值 9
1 7 8 9
>>> mixargs(d=9,c=8,a=2)
mixargs入参 "b"取默认值 7
2 7 8 9
>>> mixargs(5,d=9,c=8)
mixargs入参 "b"取默认值 7
5 7 8 9
>>> mixargs(11,d=9,c=8)
mixargs入参"a"的值 11 不在[1,10]范围内
>>> mixargs(2,d=91,c=8)
mixargs入参 "b"取默认值 7
mixargs入参"d"的值 91 不在[5,50]范围内