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

python是不带静态检查的动态语言,有时候需要在调用函数时保证参数合法。检查参数合法性是一个显著的切面场景,各个函数都可能有这个需求。但另一方面,参数合法性是不是应该由调用方来保证比较好也是一个需要结合实际才能回答的问题,总之双方约定好,不要都不检查或者都检查就可以了。下面这个模块用于在函数上使用装饰器进行参数的合法性验证。

你可以直接执行这个模块进行测试,如果完全没有输出则表示通过。你也可以找到几个以_test开头的函数,所有的测试用例都包含在这几个函数中。使用方法参见模块文档和测试用例。

# -*- 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<x<20'))
验证一个长度小于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<x<20')))
也可以简写为:
@validParam(i=nullOk(int, '10<x<20'))

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<x<20'))
  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<x<20'))
  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()

(0)

相关推荐

  • Python装饰器实现几类验证功能做法实例

    最近新需求来了,要给系统增加几个资源权限.尽量减少代码的改动和程序的复杂程度.所以还是使用装饰器比较科学 之前用了一些登录验证的现成装饰器模块.然后仿写一些用户管理部分的权限装饰器. 比如下面这种 def permission_required(permission): def decorator(f): @wraps(f) def decorated_function(*args, **kwargs): if not current_user.can(permission): abort(40

  • python类装饰器用法实例

    本文实例讲述了python类装饰器用法.分享给大家供大家参考.具体如下: #!coding=utf-8 registry = {} def register(cls): registry[cls.__clsid__] = cls return cls @register class Foo(object): __clsid__ = '123-456' def bar(self): pass print registry 运行结果如下: {'123-456': <class '__main__.F

  • Python中的装饰器用法详解

    本文实例讲述了Python中的装饰器用法.分享给大家供大家参考.具体分析如下: 这里还是先由stackoverflow上面的一个问题引起吧,如果使用如下的代码: 复制代码 代码如下: @makebold @makeitalic def say():    return "Hello" 打印出如下的输出: <b><i>Hello<i></b> 你会怎么做?最后给出的答案是: 复制代码 代码如下: def makebold(fn):    

  • 简单理解Python中的装饰器

    Python的装饰器可以实现在代码运行期间修改函数的上下文, 即可以定义函数在执行之前进行何种操作和函数执行后进行何种操作, 而函数本身并没有任何的改变. 首先, 我们先定义一个函数, 这个函数可以输出我的个人昵称: def my_name(): print "Yi_Zhi_Yu" my_name() # Yi_Zhi_Yu 那假如我需要在个人昵称输出前, 在输出我的个人uid呢, 当然, 要求是不改动现有的my_name函数, 这个时候就可以使用装饰器了 首先, 装饰器也是个函数,

  • Python中的各种装饰器详解

    Python装饰器,分两部分,一是装饰器本身的定义,一是被装饰器对象的定义. 一.函数式装饰器:装饰器本身是一个函数. 1.装饰函数:被装饰对象是一个函数 [1]装饰器无参数: a.被装饰对象无参数: 复制代码 代码如下: >>> def test(func):     def _test():         print 'Call the function %s().'%func.func_name         return func()     return _test >

  • 使用python装饰器验证配置文件示例

    根据不同配置文件调用不同的验证函数检查输入.可以根据需求更改验证函数的逻辑. 复制代码 代码如下: def VerifyData(func): def VerifyInt(data):        assert(int(data) > 0) def VerifyString(data):        assert(len(data) > 10) def inner(*args, **kvargs): print args        print kvargs assert(len(arg

  • python装饰器使用实例详解

    这篇文章主要介绍了python装饰器使用实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 python装饰器的作用就是在不想改变原函数代码的情况下,增加新的功能.主要应用了python闭包的概念,现在用1个小例子说明 import time def foo(): time.sleep(1) def bar(): time.sleep(2) def show_time(f): def inner(): start_time = time.t

  • Python装饰器用法实例总结

    本文实例讲述了Python装饰器用法.分享给大家供大家参考,具体如下: 一.装饰器是什么 python的装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.简单的说装饰器就是一个用来返回函数的函数. 它经常用于有切面需求的场景,比如:插入日志.性能测试.事务处理.缓存.权限校验等场景.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用. 概括的讲,装饰器的作用就是为已经

  • Python装饰器用法实例分析

    本文实例讲述了Python装饰器用法.分享给大家供大家参考,具体如下: 无参数的装饰器 #coding=utf-8 def log(func): def wrapper(): print 'before calling ',func.__name__ func() print 'end calling ',func.__name__ return wrapper @log def hello(): print 'hello' @log def hello2(name): print 'hello

  • Python装饰器使用实例:验证参数合法性

    python是不带静态检查的动态语言,有时候需要在调用函数时保证参数合法.检查参数合法性是一个显著的切面场景,各个函数都可能有这个需求.但另一方面,参数合法性是不是应该由调用方来保证比较好也是一个需要结合实际才能回答的问题,总之双方约定好,不要都不检查或者都检查就可以了.下面这个模块用于在函数上使用装饰器进行参数的合法性验证. 你可以直接执行这个模块进行测试,如果完全没有输出则表示通过.你也可以找到几个以_test开头的函数,所有的测试用例都包含在这几个函数中.使用方法参见模块文档和测试用例.

  • python 装饰器功能以及函数参数使用介绍

    简单的说:装饰器主要作用就是对函数进行一些修饰,它的出现是在引入类方法和静态方法的时候为了定义静态方法出现的.例如为了把foo()函数声明成一个静态函数 复制代码 代码如下: class Myclass(object): def staticfoo(): ............ ............ staticfoo = staticmethod(staticfoo) 可以用装饰器的方法实现: 复制代码 代码如下: class Myclass(object): @staticmethod

  • Python多层装饰器用法实例分析

    本文实例讲述了Python多层装饰器用法.分享给大家供大家参考,具体如下: 前言 Python 的装饰器能够在不破坏函数原本结构的基础上,对函数的功能进行补充.当我们需要对一个函数补充不同的功能,可能需要用到多层的装饰器.在我的使用过程中,遇到了两种装饰器层叠的情况,这里把这两种情况写下来,作为踩坑记录. 情况1 def A(funC): def decorated_C(funE): def decorated_E_by_CA(*args, **kwargs): out = funC(funE)

  • Python 装饰器实现DRY(不重复代码)原则

    Python装饰器是一个消除冗余的强大工具.随着将功能模块化为大小合适的方法,即使是最复杂的工作流,装饰器也能使它变成简洁的功能. 例如让我们看看Django web框架,该框架处理请求的方法接收一个方法对象,返回一个响应对象: def handle_request(request): return HttpResponse("Hello, World") 我最近遇到一个案例,需要编写几个满足下述条件的api方法: 返回json响应 如果是GET请求,那么返回错误码 做为一个注册api

  • Python装饰器(decorator)定义与用法详解

    本文实例讲述了Python装饰器(decorator)定义与用法.分享给大家供大家参考,具体如下: 什么是装饰器(decorator) 简单来说,可以把装饰器理解为一个包装函数的函数,它一般将传入的函数或者是类做一定的处理,返回修改之后的对象.所以,我们能够在不修改原函数的基础上,在执行原函数前后执行别的代码.比较常用的场景有日志插入,事务处理等. 装饰器 最简单的函数,返回两个数的和 def calc_add(a, b): return a + b calc_add(1, 2) 但是现在又有新

  • Python装饰器原理与用法分析

    本文实例讲述了Python装饰器原理与用法.分享给大家供大家参考,具体如下: 1.装饰器的本质是函数,主要用来装饰其他函数,也就是为其他函数添加附加功能 2.装饰器的原则: (1) 装饰器不能修改被装饰的函数的源代码 (2) 装饰器不能修改被装饰的函数的调用方式 3.实现装饰器的知识储备 (1) Python中函数即'变量' a.变量在Python中的存储 x='Tomwenxing' y=x [说明]: 当Python解释器遇到语句x='Tomwenxing'时,它主要完成了两样工作: 1.在

  • Python装饰器用法示例小结

    本文实例讲述了Python装饰器用法.分享给大家供大家参考,具体如下: 下面的程序示例了python装饰器的使用: 示例一: def outer(fun): print fun def wrapper(arg): result=fun(arg) print 'over!' return result return wrapper @outer def func1(arg): print 'func1',arg return 'very good!' response=func1('python'

随机推荐