在构建测试用例集时,经常须要编写一些函数,这些函数接受基本相同的参数,仅有一个参数有所差别,而且处理模式也很是相同。可使用Python闭包来定义模板函数,而后经过参数调节来自动化生产不一样的函数。html
看以下代码:python
def commonGenerate(startTime, endTime, field, values): reqs = [] for val in values: requestId = str(startTime) + "_" + str(endTime) + "_" + val baseReq = json.loads(baseExportReqStr) baseReq['osp']['start_time'] = startTime baseReq['osp']['end_time'] = endTime baseReq['osp'][field] = [val] baseReq['request_id'] = requestId reqs.append(json.dumps(baseReq)) return reqs def generateReqByState(startTime, endTime): states = ["S1", "S2", "S3", "S4", "S5", "S6"] return commonGenerate(startTime, endTime, 'state', states) def generateReqByOrderType(startTime, endTime): orderTypes = ["T1", "T2", "T3"] return commonGenerate(startTime, endTime, 'type', orderTypes) def generateReqByExpressType(startTime, endTime): expressTypes = ["E1", "E2", "E3"] return commonGenerate(startTime, endTime, 'expr_type', expressTypes) def generateReqByFeedback(startTime, endTime): feedbacks = ["F1", "F2"] return commonGenerate(startTime, endTime, 'fb', feedbacks) def getGenerateFuncs(): gvars = globals() return [ gvars[var] for var in gvars if var.startswith('generateReq') ] caseGenerateFuncs = getGenerateFuncs() print caseGenerateFuncs
这里已经抽离出通用函数 commonGenerate ,在此基础上定义了多个 generateReqByXXX ,这些函数的模式基本相同,无非是传一个字段名及值列表,而后生成一个不同的函数。 那么,是否能够作成可配置化呢: 只要给定一个 map[字段名,值列表], 就能自动生成这些函数 ?express
使用闭包能够达到这个目标。见以下代码所示:编程
def commonGenerator(startTime, endTime, field, values): def generateReqInner(startTime, endTime): reqs = [] for val in values: requestId = str(startTime) + "_" + str(endTime) + "_" + val baseReq = json.loads(baseExportReqStr) baseReq['osp']['start_time'] = startTime baseReq['osp']['end_time'] = endTime baseReq['osp'][field] = [val] baseReq['request_id'] = requestId reqs.append(json.dumps(baseReq)) return reqs return generateReqInner def generateGenerators(startTime, endTime, configs): gvars = globals() for (field, values) in configs.iteritems(): gvars['generateReqBy' + field] = commonGenerator(startTime, endTime, field, values) configs = {"state": ["S1", "S2", "S3", "S4", "S5", "S6"], \ "type": ["T1", "T2", "T3"], \ "expr_type": ["E1", "E2", "E3"], \ "fb": ["F1", "F2"] } def getGenerateFuncs(): gvars = globals() return [ gvars[var] for var in gvars if var.startswith('generateReq') ] generateGenerators(startTime, endTime, configs) caseGenerateFuncs = getGenerateFuncs() print caseGenerateFuncs
这里函数 commonGenerator 对 commonGenerate 作了一点改动,再也不直接返回值列表,而是根据不一样的参数返回一个处理不一样的函数,这个函数会返回值列表; 而后 generateGenerators 根据指定配置 configs, 调用 commonGenerator 来批量生产generateReqByXXX函数。妙不妙,生产函数的函数 !json
按维基的解释: 闭包是引用了自由变量的函数。在例子中,闭包就是 generateReqInner , 引用了传入的自由变量 field, values, 从而在 commonGenerator 调用结束以后,generateReqInner 依然存在可以被访问,且功能效果等同于 generateReqInner(startTime, endTime, field, values) 。闭包
知乎上有句话颇有启发性: 闭包就是一种特殊性质的数据,只不过这种数据刚好是携带了数据的代码块,是一个潜伏起来的随时待执行的完整的对象体(数据-行为绑定的执行体)。从这个角度来讲,也能够理解闭包的实现:app
若只访问自身里的参数及局部变量,就是普通代码块;当封闭做用域结束后,里面的一切都会被销毁; 若是这个代码块,除了访问自身的参数及局部变量,还访问在它以外的封闭做用域里的变量,那么,这个普通代码块就升级为闭包,其访问的自由变量和这个代码块将会共同保存并独立于封闭做用域的存在; 当封闭做用域结束后,这个闭包不会一同消亡,而是继续独立存在。 例子中,generateReqInner 访问了其做用域以外的封闭做用域里的参数 field, values, 从而变成了独立于commonGenerator 的闭包。函数式编程
一个简单而经典的例子以下:函数
def outer(): def inner(): count = [1] print 'inner: ', count[0] count[0] += 1 return inner def outer2(): count = [1] def inner2(): print 'inner2: ', count[0] count[0] += 1 return inner2 def outer3(alist): inners = [] for e in alist: def inner3(): print 'inner3: ', e inners.append(inner3) return inners def outer4(alist): inners = [] for e in alist: def inner4(g): def inner(): print 'inner4: ', g return inner inners.append(inner4(e)) return inners if __name__ == '__main__': inner = outer() inner() inner() inner2 = outer2() inner2() inner2() for outer in [outer3, outer4]: inners = outer([1,2,3]) for inner in inners: inner() ''' output inner: 1 inner: 1 inner2: 1 inner2: 2 inner3: 3 inner3: 3 inner3: 3 inner4: 1 inner4: 2 inner4: 3 '''
在 inner 中,只访问了本身的局部变量;当调用 inner = outer() 后, inner 是一个普通函数,每次调用时 count 都会从新建立为 count = [1] ; 而在 inner2 中,访问了outer2 的变量 count, 这个变量在 inner2 的做用域外,成为了一个闭包。 当调用 inner2 = outer2() 后,inner2 存储了自由变量 count ,并在每次调用后都会增长 count[0],从而使得每次打印的值都不一样。 注意,若是每次都这样调用 outer2()() ,其效果与 outer()() 是同样的,count 不会变化。所以,从某种意义来讲,闭包更像是个动态的执行体,而不是静态的。测试
outer3 展现了使用闭包的一个注意事项,虽然也采用了闭包,可是闭包存的是循环结束后的最终值;若是要每一个函数分别存储循环变量的每一个值,就须要将循环变量做为封闭做用域的参数传给闭包。
闭包的一大应用是做为函数工厂,能够批量生产函数,模拟柯里化效果。柯里化的基本介绍可参阅博文:“函数柯里化(Currying)示例”。闭包 closure(x,y) = closure(x)(y) ,当传入不一样的 y 时,就能生产不一样的函数。好比幂次方求和函数 p(n,m) = 1^m + 2^m + ... + n^m ;p(n,1) 就是列表求和;p(n,2) 就是平方和; p(n,3) 就是立方和。 代码以下所示:
def p(alist,m): return sum(map(lambda x: x**m, alist)) def pclosure(alist, m): if m: return lambda l: sum(map(lambda x: x**m, l)) if alist: return lambda n: sum(map(lambda x: x**n, alist)) return lambda l,n: sum(map(lambda x: x**n, l)) def getlist(n): return map(lambda x:x+1, range(n)) msum = pclosure([], 1) print 'sum([1-3]^1) = ', msum(getlist(3)) print 'sum([1-5]^1) = ', msum(getlist(5)) msum = pclosure([], 2) print 'sum([1-3]^2) = ', msum(getlist(3)) print 'sum([1-5]^2) = ', msum(getlist(5)) mpower = pclosure(getlist(10), None) print 'sum([1-10]^1) = ', mpower(1) print 'sum([1-10]^3) = ', mpower(3) plain = pclosure(None, None) print 'sum([1-8]^1) = ', plain(getlist(8), 1) print 'sum([1-8]^2) = ', plain(getlist(8), 2) ''' output sum([1-3]^1) = 6 sum([1-5]^1) = 15 sum([1-3]^2) = 14 sum([1-5]^2) = 55 sum([1-10]^1) = 55 sum([1-10]^3) = 3025 sum([1-8]^1) = 36 sum([1-8]^2) = 204 '''
p 是一个普通的实现,每次都必须指定一个列表alist和一个数值m ;与之对应的是 pclosure 的实现。若是没有提供列表而提供了幂次 M,就返回一个函数,这个函数接受列表,求指定幂次的和 pclosure(alist, m) = pclosure(alist, M) , M 已指定; 若是提供了列表 LIST 而没有提供幂次 m ,就返回一个函数,这个函数接受一个幂次,对列表的指定幂次求和 pclosure(alist, m) = pclosure(LIST, m) , LIST 已指定;若是列表和幂次都没有提供,就退回到一个普通的二元函数p(alist,m) 分别指定不一样的参数,就能生成不一样种类的一类函数。是否是颇有意思?
经过Python闭包结合配置自动生成函数,使得代码表达能力更强大了。结合函数式编程,其威力可拭目以待。