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 (三) 修改
package cn.sasa.demo3; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Pr ...
- LDA学习小记
看到一段对主题模型的总结,感觉很精辟: 如何找到文本隐含的主题呢?常用的方法一般都是基于统计学的生成方法.即假设以一定的概率选择了一个主题,然后以一定的概率选择当前主题的词.最后这些词组成了我们当前的 ...
- eslint 代码检测工具
jshint 检测工具不够灵活下,道格拉斯(何许人也?json创造者,javascript重要任务,犀牛那本书就是他写的). 文档地址: 中文地址 English 安装 利用npm全局安装eslint ...
- VS2013 opencv配置
有三个地方需要配置,在配置之前首先要将platform配置好,下面的例子是x64 Release的“ 然后需要将include.lib的路径配置好 然后将dll拷贝至编译生成的Release文件夹中即 ...
- es内部的多线程异步并发控制
version元数据(1)第一次创建一个document的时候,它的_version版本号是1:以后,每次对这个document执行修改或者删除操作,都会对这个_version版本号自动加1(2)在删 ...
- wordpress如何屏蔽wp-json(禁用REST API)
最近网友问ytkah怎么在网站日志文件中发现蜘蛛爬行了很多次的/wp-json/目录,在robots文件中disallow掉了爬虫还是访问了那个目录,能不能直接在程序中直接改呢?通过查询相关文档发现W ...
- blockdev命令 blkid命令 lsblk命令
blockdev命令 blkid命令 lsblk命令 http://www.jb51.net/LINUXjishu/310389.html block相关的命令 这篇文章主要介绍了Linux bl ...
- MYSQL的价格
MYSQL的价格 来自:http://www.greatlinux.com/column/column.do?nodeid=2c90c6093416705c013416f283f40004&c ...
- 20170719 Mysql 配置远端Mysql访问,增加表/存储过程
-- 1 .在windows 环境中安装Mysql 会按照到默认的C盘当中,如何修改呢--? -- 2. 如何只安装客户端不安装Mysql 数据库服务 --? -- 3. 表的特殊列,默认采用函数值 ...
- Python3学习之路~3.2 递归、函数式编程、高阶函数、匿名函数、嵌套函数
1 递归 在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数. def calc(n): print(n) if int(n / 2) == 0: return n r ...