python参数验证_Python装饰器使用实例:验证参数合法性

本文介绍了一个Python装饰器`validParam`的使用,用于验证函数参数的合法性。装饰器支持验证参数类型、条件表达式、允许None值以及多类型验证。通过示例展示了如何在函数中应用这些验证规则,确保参数符合预期要求。
摘要由CSDN通过智能技术生成

# -*- coding: UTF-8 -*-

'''

@summary: 验证器

该模块提供了一个装饰器用于验证参数是否合法,使用方法为:

from validator import validParam, nullOk, multiType

@validParam(i=int)

def foo(i):

return i+1

编写验证器:

1. 仅验证类型:

@validParam(type, ...)

例如:

检查第一个位置的参数是否为int类型:

@validParam(int)

检查名为x的参数是否为int类型:

@validParam(x=int)

验证多个参数:

@validParam(int, int)

指定参数名验证:

@validParam(int, s=str)

针对*和**参数编写的验证器将验证这些参数实际包含的每个元素:

@validParam(varargs=int)

def foo(*varargs): pass

@validParam(kws=int)

def foo7(s, **kws): pass

2. 带有条件的验证:

@validParam((type, condition), ...)

其中,condition是一个表达式字符串,使用x引用待验证的对象;

根据bool(表达式的值)判断是否通过验证,若计算表达式时抛出异常,视为失败。

例如:

验证一个10到20之间的整数:

@validParam(i=(int, '10

验证一个长度小于20的字符串:

@validParam(s=(str, 'len(x)<20'))

验证一个年龄小于20的学生:

@validParam(stu=(Student, 'x.age<20'))

另外,如果类型是字符串,condition还可以使用斜杠开头和结尾表示正则表达式匹配。

验证一个由数字组成的字符串:

@validParam(s=(str, '/^\d*$/'))

3. 以上验证方式默认为当值是None时验证失败。如果None是合法的参数,可以使用nullOk()。

nullOk()接受一个验证条件作为参数。

例如:

@validParam(i=nullOk(int))

@validParam(i=nullOk((int, '10

也可以简写为:

@validParam(i=nullOk(int, '10

4. 如果参数有多个合法的类型,可以使用multiType()。

multiType()可接受多个参数,每个参数都是一个验证条件。

例如:

@validParam(s=multiType(int, str))

@validParam(s=multiType((int, 'x>20'), nullOk(str, '/^\d+$/')))

5. 如果有更复杂的验证需求,还可以编写一个函数作为验证函数传入。

这个函数接收待验证的对象作为参数,根据bool(返回值)判断是否通过验证,抛出异常视为失败。

例如:

def validFunction(x):

return isinstance(x, int) and x>0

@validParam(i=validFunction)

def foo(i): pass

这个验证函数等价于:

@validParam(i=(int, 'x>0'))

def foo(i): pass

@author: HUXI

@since: 2011-3-22

@change:

'''

import inspect

import re

class ValidateException(Exception): pass

def validParam(*varargs, **keywords):

'''验证参数的装饰器。'''

varargs = map(_toStardardCondition, varargs)

keywords = dict((k, _toStardardCondition(keywords[k]))

for k in keywords)

def generator(func):

args, varargname, kwname = inspect.getargspec(func)[:3]

dctValidator = _getcallargs(args, varargname, kwname,

varargs, keywords)

def wrapper(*callvarargs, **callkeywords):

dctCallArgs = _getcallargs(args, varargname, kwname,

callvarargs, callkeywords)

k, item = None, None

try:

for k in dctValidator:

if k == varargname:

for item in dctCallArgs[k]:

assert dctValidator[k](item)

elif k == kwname:

for item in dctCallArgs[k].values():

assert dctValidator[k](item)

else:

item = dctCallArgs[k]

assert dctValidator[k](item)

except:

raise ValidateException,\

('%s() parameter validation fails, param: %s, value: %s(%s)'

% (func.func_name, k, item, item.__class__.__name__))

return func(*callvarargs, **callkeywords)

wrapper = _wrapps(wrapper, func)

return wrapper

return generator

def _toStardardCondition(condition):

'''将各种格式的检查条件转换为检查函数'''

if inspect.isclass(condition):

return lambda x: isinstance(x, condition)

if isinstance(condition, (tuple, list)):

cls, condition = condition[:2]

if condition is None:

return _toStardardCondition(cls)

if cls in (str, unicode) and condition[0] == condition[-1] == '/':

return lambda x: (isinstance(x, cls)

and re.match(condition[1:-1], x) is not None)

return lambda x: isinstance(x, cls) and eval(condition)

return condition

def nullOk(cls, condition=None):

'''这个函数指定的检查条件可以接受None值'''

return lambda x: x is None or _toStardardCondition((cls, condition))(x)

def multiType(*conditions):

'''这个函数指定的检查条件只需要有一个通过'''

lstValidator = map(_toStardardCondition, conditions)

def validate(x):

for v in lstValidator:

if v(x):

return True

return validate

def _getcallargs(args, varargname, kwname, varargs, keywords):

'''获取调用时的各参数名-值的字典'''

dctArgs = {}

varargs = tuple(varargs)

keywords = dict(keywords)

argcount = len(args)

varcount = len(varargs)

callvarargs = None

if argcount <= varcount:

for n, argname in enumerate(args):

dctArgs[argname] = varargs[n]

callvarargs = varargs[-(varcount-argcount):]

else:

for n, var in enumerate(varargs):

dctArgs[args[n]] = var

for argname in args[-(argcount-varcount):]:

if argname in keywords:

dctArgs[argname] = keywords.pop(argname)

callvarargs = ()

if varargname is not None:

dctArgs[varargname] = callvarargs

if kwname is not None:

dctArgs[kwname] = keywords

dctArgs.update(keywords)

return dctArgs

def _wrapps(wrapper, wrapped):

'''复制元数据'''

for attr in ('__module__', '__name__', '__doc__'):

setattr(wrapper, attr, getattr(wrapped, attr))

for attr in ('__dict__',):

getattr(wrapper, attr).update(getattr(wrapped, attr, {}))

return wrapper

#===============================================================================

# 测试

#===============================================================================

def _unittest(func, *cases):

for case in cases:

_functest(func, *case)

def _functest(func, isCkPass, *args, **kws):

if isCkPass:

func(*args, **kws)

else:

try:

func(*args, **kws)

assert False

except ValidateException:

pass

def _test1_simple():

#检查第一个位置的参数是否为int类型:

@validParam(int)

def foo1(i): pass

_unittest(foo1,

(True, 1),

(False, 's'),

(False, None))

#检查名为x的参数是否为int类型:

@validParam(x=int)

def foo2(s, x): pass

_unittest(foo2,

(True, 1, 2),

(False, 's', 's'))

#验证多个参数:

@validParam(int, int)

def foo3(s, x): pass

_unittest(foo3,

(True, 1, 2),

(False, 's', 2))

#指定参数名验证:

@validParam(int, s=str)

def foo4(i, s): pass

_unittest(foo4,

(True, 1, 'a'),

(False, 's', 1))

#针对*和**参数编写的验证器将验证这些参数包含的每个元素:

@validParam(varargs=int)

def foo5(*varargs): pass

_unittest(foo5,

(True, 1, 2, 3, 4, 5),

(False, 'a', 1))

@validParam(kws=int)

def foo6(**kws): pass

_functest(foo6, True, a=1, b=2)

_functest(foo6, False, a='a', b=2)

@validParam(kws=int)

def foo7(s, **kws): pass

_functest(foo7, True, s='a', a=1, b=2)

def _test2_condition():

#验证一个10到20之间的整数:

@validParam(i=(int, '10

def foo1(x, i): pass

_unittest(foo1,

(True, 1, 11),

(False, 1, 'a'),

(False, 1, 1))

#验证一个长度小于20的字符串:

@validParam(s=(str, 'len(x)<20'))

def foo2(a, s): pass

_unittest(foo2,

(True, 1, 'a'),

(False, 1, 1),

(False, 1, 'a'*20))

#验证一个年龄小于20的学生:

class Student(object):

def __init__(self, age): self.age=age

@validParam(stu=(Student, 'x.age<20'))

def foo3(stu): pass

_unittest(foo3,

(True, Student(18)),

(False, 1),

(False, Student(20)))

#验证一个由数字组成的字符串:

@validParam(s=(str, r'/^\d*$/'))

def foo4(s): pass

_unittest(foo4,

(True, '1234'),

(False, 1),

(False, 'a1234'))

def _test3_nullok():

@validParam(i=nullOk(int))

def foo1(i): pass

_unittest(foo1,

(True, 1),

(False, 'a'),

(True, None))

@validParam(i=nullOk(int, '10

def foo2(i): pass

_unittest(foo2,

(True, 11),

(False, 'a'),

(True, None),

(False, 1))

def _test4_multitype():

@validParam(s=multiType(int, str))

def foo1(s): pass

_unittest(foo1,

(True, 1),

(True, 'a'),

(False, None),

(False, 1.1))

@validParam(s=multiType((int, 'x>20'), nullOk(str, '/^\d+$/')))

def foo2(s): pass

_unittest(foo2,

(False, 1),

(False, 'a'),

(True, None),

(False, 1.1),

(True, 21),

(True, '21'))

def _main():

d = globals()

from types import FunctionType

print

for f in d:

if f.startswith('_test'):

f = d[f]

if isinstance(f, FunctionType):

f()

if __name__ == '__main__':

_main()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值