高阶函数柯里化及装饰器的使用
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了高阶函数柯里化及装饰器的使用相关的知识,希望对你有一定的参考价值。
高阶函数
数学概念的引入:
例 y = g(f(x))
数据来源自另一个函数,y带到g函数,从而又赋值给y
高阶函数的特性
在数学和计算机科学中,高阶函数至少满足以下任意一个条件
在数学中,高阶满足以下两个条件之一:
1.接受一个或多个函数作为参数,对于g来讲的话整体算是一个自变量
例:将g做为一个自变量
y =g(f1(x),f2(x))
2.输出一个函数
输出一个值,这里的值是函数对象的帧,并非是函数返回值
参数中使用了函数对象,如果输出的是一个函数,则满足的是一个高阶函数
简单讲,参数中出现函数或者return中出现函数则都被称为高阶函数
例:
defcounter(base):
definc(step=1):
nonlocalbase
base+= 1
returnbase
returninc()
foo =counter(10)
print(foo)
foo1 =counter(11)
print(foo1)
11
12
查看foo和foo1的id地址进行比较
foo =counter(10)
print(id(foo))
foo1 =counter(11)
print(id(foo1))
494806032
494806064
因为inc是counter的某一对象,在赋值的过程中发生了id更变,所以foo 生成的对象本身不一样
可以作为一个本地的变量去了解
在堆中做对象的创建 ,堆中是乱序的,后期由虚拟机进行垃圾回收;
栈: 由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆: 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。
在堆中的对象创建,都是为inc创建的,inc赋值给foo
所以foo1 和 foo的地址不一样,当第一次调用counter;
需counter进行压栈并将参数标记压栈,创建栈帧,并创建临时对象inc
inc进行return 交给外部变量,因为最后要retrun,return以后的代码要被从栈中清空
引用是在堆上,并非在栈,inc所对应的对象需要等值进行赋予,在堆上直接引用地址,所以堆不会被清理
自定义sort方法
首先找到一个大值,遍历x的时候会取一个值并进行对列表遍历并匹配
如果列表是空的则不会进入循环,直接append到新的列表中
第一次的时候直接进入到ret中,ret现在是1,那么再获取31 再与ret中比较,ret重只有一个1
31比1大,那么在当前位置中插入元素(这里是与ret的当前位置进行索引插入)
lst =[1,31,4,7,3,21,8]
defsort(items):
ret= []
forx in items: #遍历lst
fori,y in enumerate(ret): #对下标进行标注
ifx > y: #拿出第一个元素依次对比列表中的元素,如果大于当前值则在最前面追加索引
ret.insert(i,x)
break
else:
ret.append(x)
returnret
print(sort(lst))
改进:对其添加默认参数及判断优化
lst =[1,31,4,7,3,21,8]
defsort(items,reverse=False):
ret= []
forx in items:
fori,y in enumerate(ret):
flag= x > y if not reverse else x < y #flag为标记,如果反向说明这个是true
ifflag: # 判断其布尔值
ret.insert(i,x)
break
else:
ret.append(x)
returnret
print(sort(lst))
使用flag 无非是判断reverse参数的真假值,用于正反向
print(sort(lst,reverse=False))
print(sort(lst,reverse=True))
flag= x > y if not reverse else x < y
相当于:
ifreverse:
flag= x > y
else:
flag= x < y
再次改进:
使用嵌套函数实现判断reverse
lst= [1,31,4,7,3,21,8]
defsort(items,reverse=False):
defcomp(a,b): # 用ab去接受xy 的值
flag= a > b if reverse else a < b
returnflag # 返回flag的布尔值
ret= []
forx in items:
fori,y in enumerate(ret):
ifcomp(x,y): # 判断flag的布尔值是x > y还是y<x
ret.insert(i,x)
break
else:
ret.append(x)
returnret
print(sort(lst,reverse=True))
再次改进,将通用函数放置函数外部
lst= [1,31,4,7,3,21,8]
defsort(items,reverse=False):
defcomp(a,b,reverse): #用ab去接受xy
flag= a > b if reverse else a < b
ret= []
forx in items:
fori,y in enumerate(ret):
ifcomp(x,y,reverse):
ret.insert(i,x,reverse)
break
else:
ret.append(x)
returnret
print(sort(lst,reverse=True))
再次改进:
通过参数传参调用外部函数(外部函数为一个通用函数)进行
在外部函数控制内部函数的排序逻辑,升序或者降序,这样使得我们的函数更加灵活
lst =[1,31,4,7,3,21,8]
defcomp(a,b):
returna < b
defsort(items,key=comp,reverse=False):
ret= [ ]
forx in items:
fori ,y in enumerate(ret):
flag= key(x,y) if reverse else key(y,x)
#if comp(x,y):
ifflag:
ret.insert(i,x)
break
else:
ret.append(x)
returnret
print(sort(lst,reverse=True))
使用lambda替换comp函数
defsort(lst,key=lambda a,b : a < b,reverse=False): # 通过key的传参的布尔值来控制升序或降序
ret= [ ]
forx in lst:
fork,v in enumerate(ret):
flag= key(x,k) if reverse elsekey(k,v) # 判断函数的布尔值来替换排序的位置,真为x>k 假为x < k
ifflag:
ret.insert(k,x)
break
else:
ret.append(x)
returnret
print(sort(lst,reverse=True))
print(sort(lst,reverse=False))
总结:
所谓高阶函数的参数由外界的传入;
首先基本函数成型,将其抽取参数,判断分支结构进行拆解形成外部函数
内建函数
sorted 排序
filter 过滤数据,只保留想获取的部分
map 映射
sorted 通过key做排序进行规则定义,只能做同类型的数据处理
In [7]:lst = [1,31,4,7,3,21,8]
In [8]:sorted(lst,reverse=True)
Out[8]:[31, 21, 8, 7, 4, 3, 1]
In [9]:sorted(lst,reverse=False)
Out[9]:[1, 3, 4, 7, 8, 21, 31]
In[25]: a = lambda x: 6 -x
In[26]: a(7)
Out[26]:-1
In[32]: sorted(lst,key=lambda x:31-x)
Out[32]:[31, 21, 8, 7, 4, 3, 1]
就地修改
In[37]: lst
Out[37]:[1, 31, 4, 7, 3, 21, 8]
In[38]: lst.sort()
In[39]: lst
Out[39]:[1, 3, 4, 7, 8, 21, 31]
In[46]: lst.sort(key=lambda x:1)
In[47]: lst
Out[47]:[31, 21, 8, 7, 4, 3, 1]
比较临时值,追加元素本身,key并不是修改原来元素,之是为了临时比较得出临时值
filter
只能过滤可迭代的对象元素,并返回一个迭代器
返回迭代器说明是同一份数据,function一个具有一个参数的函数,返回一个布尔值
迭代器说明做一些大量数据做一些处理,所以迭代器比较合适
In[60]: list( filter(lambda x:x%3 == 0 ,[_ for _ in range(10)]))
Out[60]:[0, 3, 6, 9]
a =filter(lambda x: x%3 == 0 ,(_ for _ in range(10)))
#print(list(a))
[0, 3,6, 9]
相当于:
lst =[1,2,3,4,5,6]
deffn(x,ret=[]):
forx in x:
ifx % 3 == 0:
ret.append(x)
returnret
print(fn(lst))
相当于我们自行编写的sort函数,一个x参数表示对每个参数进行判断并过滤,x拿到元素之后查看是否可以被3整除,因为生成器是惰性求值,所以并不会立即返回
map
map可收集n个可迭代对象,并生成迭代器
将各种类型进行迭代,可以是元组、字典、list 都可以
In[65]: lambda x:x%3==0,[2,3,4,5,6,7,8,9,11,12,13]
Out[65]:(<function __main__.<lambda>>, [2, 3, 4, 5, 6, 7, 8, 9, 11, 12,13])
In[66]: map(lambda x:x%3==0,[2,3,4,5,6,7,8,9,11,12,13])
Out[66]:<map at 0x7f22901cb278>
In [68]:dict(map(lambda x:(x,x%3==0),[2,3,4,5,6,7,8,9,11,12,13]))
Out[68]:
{2:False,
3:True,
4:False,
5:False,
6:True,
7:False,
8:False,
9:True,
11:False,
12:True,
13:False}
In[71]: dict(map(lambda x:(x%3,x),[2,3,4,5,6,7,8,9,11,12,13]))
Out[71]:{0: 12, 1: 13, 2: 11}
In[76]: dict(map(lambda x:(x+1,x),range(10)))
Out[76]:{1: 0, 2: 1, 3: 2, 4: 3, 5: 4, 6: 5, 7: 6, 8: 7, 9: 8, 10: 9}
字典生成过程必须是一个二元组
总结:
map主要是生成一个新的值,从一个元素映射到另一个元素
map 直接转换,将原来的某个操作添加封装以另一种形式返还
filter 只保留当前需要的元素,其他丢弃(python中不会立即修改,而是惰性求值,,因为返回的都是迭代器)
sorted不会就地修改
高阶函数简单来讲,在参数中出现了函数,两者满足其一则被称为高阶函数
所有的函数封装除了嵌套函数(因为需要用到闭包)都需要遵从这样的原则
#将核心逻辑改为可变的,因为函数是一等公民
柯里化
数学概念,将原来接受两个参数的函数整合为一个新的可以接受一个参数函数的过程
新的函数返回一个原有第二个参数作为参数的函数
z = f(x,y)转为 z = f(x)(y)
defadd(x):
def_add(y):
returnx + y
return_add
print(add(4)(6))
需求来了:
一个加法函数,想增强它的功能,能够被调用过以及调用的参数信息
defadd(x,y):
returnx + y
#改为:
defadd(x,y):
print(‘call,x+y‘)
returnx + y
使用__name__特殊对象来获取对象名称
funcname.__name__
defadd(x,y,file):
print(‘{},{}+ {}‘.format(add.__name__,x,y,file=file))
#print(add.__name__)
returnx + y
add(4,5,file=‘file‘)
add,4 +5
file是一个形参,后面file以关键字参数传参
首先打印语句耦合性太高,与add函数混在一起,
打印函数是否属于加法?我们需要将非业务的需求也加入进来,所以需要加入到业务函数是不合适的,这样的写法叫做侵入式
但是又想对函数进行功能性增强
对业务做出分离
defadd(x,y):
returnx + y
deflogger(fn):
print(‘before‘)
ret= fn(4,5)
print(‘after‘)
returnret
logger(add)
但这里的参数依旧是写死的,改进:
defadd1(x,y):
returnx + y
defadd2(x,y,z):
returnx + y + z
deflogger(fn,*args,**kwargs):
ret= fn(*args,**kwargs)
returnret
print(logger(add2,3,4,5))
再次改进:
定义一个函数,进行运算,通过闭包方式调用add进行传参
并返回结果
defadd(x,y,z):
returnx + y + z
deflogger(fn,*args,**kwargs):
def_logger(*args,**kwargs):
ret= fn(*args,*kwargs)
returnret
return_logger
print(logger(add)(1,2,3))
引用装饰器
等于add1 = logger(add1)
对比:
defadd(x,y,z):
returnx + y + z
deflogger(fn,*args,**kwargs):
def_logger(*args,**kwargs):
return fn(*args,*kwargs)
return_logger
print(logger(add)(1,2,3))
修改为:
deflogger(fn):
def_logger(*args): #形成一个闭包
print(‘123 43 4‘) #为了看到效果并不是直接调用add函数,在这里直接打印一些内容作为标记
returnfn(*args)
return_logger
@logger
defadd1(*args):
i= 0
forx in args:
i+= x
returni
print(add1(1,2,3,4))
12 3 434
10
装饰器 等于 add = logger(add)
装饰器只能接一个参数,参数就是add标识符
装饰器语法
无参装饰器
·它是一个函数
·函数作为它的参数
·返回值也必须是一个函数
deflogger(fn):
defwapper(*args):
print(‘begin‘)
x= fn(*args)
print(‘end‘)
returnx
returnwapper
@logger
defadd(x,y):
returnx + y
print(add(1,2))
装饰器肯定是一个高阶函数,不断传入函数,并返回一个内层函数
总结对比
defadd1(*args):
i= 0
forx in args:
i+= x
returni
#########################################
deftest(fn):
def_test(*args):
print(111111)
returnfn(*args)
return_test
@test
defabc(*args):
i= 0
forx in args:
i+= x
returni
print(abc(1,2,3))
文档字符串
__doc__string
使用要求:在函数第一个表达式,必须是个字符串
defadd1(x,y):
ret= x,y
returnret
add1(1,100)
print(add1.__name__)
add1
defadd1(x,y):
‘‘‘hellopython
enenenen‘‘‘
ret= x,y
returnret
add1(1,100)
print(add1.__doc__)
hellopython
enenenen
函数的帮助本身就是调用__doc__()
使用装饰器改logger,添加说明
deflogger(fn):
def_logger(*args):
‘thiis logger‘
print(‘logger‘)
returnfn(*args)
return_logger
@logger
defadd(x,y):
returnx + y
print(add.__name__,add.__doc__,sep=‘\n‘)
_logger
thi islogger
看到这里打印的是logger函数内的信息
name和doc是对象的属性,所以,我们可以通过一个新的函数对其进行控制
定义一个新的函数用于标记被调函数的属性
defcopy_pro(src,dst):
dst.__name__= src.__name__
src.__doc__= dst.__doc__
defcopy_pro(src,dst):
dst.__name__= src.__name__
dst.__doc__= src.__doc__
deflogger(fn):
def_logger(*args):
‘thiis logger‘
print(‘logger‘)
returnfn(*args)
copy_pro(fn,_logger)
return_logger
@logger
defadd(x,y):
‘hahaha‘
returnx + y
print(add.__name__,add.__doc__,sep=‘\n‘)
add
hahaha
将 _logger 的__doc__ 和 __name__ 指向了 add.__name__ , add__doc__
logger定义过了,源已经赋值给了目的,src被带入了fn也就是add,doc是add 字符串
这样就把属性改为一样的
__qualname__ 是函数的限定名称
print(add.__qualname__,sep=‘\n‘)
logger.<locals>._logger
简化:将拷贝函数做为装饰器进行封装
被包装函数的文档等一切属性则会消失
改造:带参装饰器
提供一个函数,被封装函数属性copy() 包装函数属性
defcopy_pro(src):
def_copy(dst):
dst.__name__= src.__name__
dst.__doc__= src.__doc__
dst.__qualname__= src.__qualname__
returndst
return_copy
deflogger(fn):
@copy_pro(fn)
defwrapper(*args,**kwargs):
x= fn(*args,**kwargs)
returnx
returnwrapper
@logger
defadd(x,y):
returnx + y
print(add(1,2))
print(add.__qualname__)
python本身提供了拷贝函数
通过自定义函数将被包装函数属性覆盖掉包装函数
凡被装饰装饰的函数都需要复制这些属性,比较通用
可以将复制属性的函数构建成装饰器函数 -- 带参装饰器
带参装饰器,带参装饰器无非加了一层函数
例:将大于50秒的语句筛出
参数值只能传一个,所以需要与其替换次序
importdatetime
importtime
deflogger(t):
def_logger(fn):
defwrap(*args,**kwargs):
start= datetime.datetime.now()
ret= fn(*args,**kwargs)
duration= int((datetime.datetime.now() - start).total_seconds())
ifduration > t:
print("{}took {}".format(fn.__name__,duration.total_seconds()))
returnret
returnwrap
return_logger
@logger(30)
defadd(x,y):
time.sleep(5)
returnx + y
print(add(33,y=7))
以上就可以将通过外部提供的函数来灵活的控制输出
functools模块
import functools
deflogger(fn):
@functools.wraps(fn)
def_logger(*args):
‘thiis logger‘
print(‘logge!!!!!!!!!!!r‘)
returnfn(*args)
#copy_pro(fn,_logger)
return_logger
@logger
defadd(x,y):
‘hahaha‘
returnx + y
print(add(2,3))
print(add.__qualname__,add.__doc__,sep=‘\n‘)
logge!!!!!!!!!!!r
5
add
hahaha
本文出自 “心情依旧” 博客,请务必保留此出处http://yijiu.blog.51cto.com/433846/1975437
以上是关于高阶函数柯里化及装饰器的使用的主要内容,如果未能解决你的问题,请参考以下文章