Python使用闭包结合配置自动生成函数
背景###
在构建测试用例集时,常常需要编写一些函数,这些函数接受基本相同的参数,仅有一个参数有所差异,并且处理模式也非常相同。可以使用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[字段名,值列表], 就能自动生成这些函数 ?
使用闭包可以达到这个目标。见如下代码所示:
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函数。妙不妙,生产函数的函数 !
闭包###
理解####
按维基的解释: 闭包是引用了自由变量的函数。在例子中,闭包就是 generateReqInner , 引用了传入的自由变量 field, values, 从而在 commonGenerator 调用结束之后,generateReqInner 依然存在能够被访问,且功能效果等同于 generateReqInner(startTime, endTime, field, values) 。
知乎上有句话很有启发性: 闭包就是一种特殊性质的数据,只不过这种数据恰好是携带了数据的代码块,是一个潜伏起来的随时待执行的完整的对象体(数据-行为绑定的执行体)。从这个角度来说,也可以理解闭包的实现:
- 要件一: 闭包必定存在于一个封闭的作用域 D; 例子中这个 D 就是 commonGenerator 函数的作用域;
- 要件二: 处于封闭作用域的代码块访问了在代码块作用域之外的封闭作用域里的自由变量。
若只访问自身里的参数及局部变量,就是普通代码块;当封闭作用域结束后,里面的一切都会被销毁; 如果这个代码块,除了访问自身的参数及局部变量,还访问在它之外的封闭作用域里的变量,那么,这个普通代码块就升级为闭包,其访问的自由变量和这个代码块将会共同保存并独立于封闭作用域的存在; 当封闭作用域结束后,这个闭包不会一同消亡,而是继续独立存在。 例子中,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闭包结合配置自动生成函数,使得代码表达能力更强大了。结合函数式编程,其威力可拭目以待。
Python使用闭包结合配置自动生成函数的更多相关文章
- 在Python命令行和VIM中自动补全
作者:gnuhpc 出处:http://www.cnblogs.com/gnuhpc/ 1. VIM下的配置: wget https://github.com/rkulla/pydiction/arc ...
- python编写工具及配置(notepad++)
学长跟我说老师实验室里用的ide是pycharm,我用了一天,整体还行,就是加载速度太慢,可是第二天用的时候就卡的想让人骂街,cpu占有率趋近100%,电脑配置不高,我寻思不能因为这个就马上换电脑吧, ...
- python的开发环境配置-Eclipse-PyDev插件安装
安装PyDev插件的两种安装方法: 1.百度搜索PyDev 2.4.0.zip,下载后解压,得到Plugins和Feature文件夹,复制两文件夹到Eclipse目录,覆盖即可. 插件的版本要对应py ...
- python开发环境安装配置
需要安装的软件: Python2.7.14和Python3.6.4 要在电脑上同时安装两个版本 开发工具:PyCharm 是一个jetbrains的python开发工具 idea系列之一 Pyt ...
- 巧用Salt,实现CMDB配置自动发现
随着互联网+新形势的发展,越来越多的企业步入双态(稳敏双态)IT时代,信息化环境越来越复杂,既有IOE三层架构,也有VCE.Openstack等云虚拟化架构和互联网化的分布式大数据架构.所以,企业急需 ...
- sublime txet 3 python 开发环境安装配置
下载python 下载地址:https://www.python.org/downloads/windows/ 下载sublime text 3 下载地址:https://www.sublimetex ...
- Python函数编程——闭包和装饰器
Python函数编程--闭包和装饰器 一.闭包 关于闭包,即函数定义和函数表达式位于另一个函数的函数体内(嵌套函数).而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量.参数.当其中一个 ...
- python的安装与配置
pyhton的下载与安装 1.python官网地址:https://www.python.org 2.下载 Python 编辑器PyCharm PyCharm 是一款功能强大的 Python 编辑器 ...
- jenkins结合supervisor进行python程序发布后的自动重启
jenkins结合supervisor进行python程序发布后的自动重启 项目背景: 通过jenkins发布kvaccount.chinasoft.com站点的python服务端程序,业务部门同事需 ...
随机推荐
- java JDBC (七) org.apache.commons.dbutils 查询
package cn.sasa.demo1; import java.sql.Connection; import java.sql.SQLException; import java.util.Li ...
- java 集合(三)List接口
package cn.sasa.demo1; import java.util.ArrayList; import java.util.LinkedList; import java.util.Lis ...
- 如何在win+r 或者是win10的应用搜索输入subl就能打开sublime
这虽然不是什么技术贴,我实在不想开启sublime还要动鼠标,或者输入subl长长的全称,这里有两种做法: 第一种 在环境变量添加sublime安装目录的变量,一般sublime的安装目录会有subl ...
- FastDFS的使用
1.FastDFS 1.1. 什么是FastDFS? FastDFS是用c语言编写的一款开源的分布式文件系统.FastDFS为互联网量身定制,充分考虑了冗余备份.负载均衡.线性扩容等机制,并注重高可用 ...
- mysql批量插入数据
建表 create table `dept`( `id` ) unsigned NOT NULL AUTO_INCREMENT, `deptno` mediumint() unsigned ', `d ...
- protocol error, got 'n' as reply type byte + redis如何后台启动
其它机子的PHP访问redis爆“protocol error, got 'n' as reply type byte ”错误 解决办法: 在redis配置文件redis.conf中注释掉bind配置 ...
- [django]django corepython核心编程
model meta排序 class BlogPost(models.Model): title = models.CharField(max_length=150) body = models.Te ...
- 项目发布脚本-nodejs
#!/bin/bash export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin clear printf &q ...
- SQL 的约束
说明:文章所有内容均截选自用户"实验楼包工头"发布在实验楼上的教程[MySQL 基础课程],想要详细的学习SQL,点击教程即可免费学习了:未经允许,禁止转载: 约束是一种限制,它通 ...
- mac 安装geckodriver和chromedriver
Last login: Fri Apr :: on ttys000 (base) localhost:~ ligaijiang$ env TERM_PROGRAM=Apple_Terminal SHE ...