扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
专业成都网站建设公司,做排名好的好网站,排在同行前面,为您带来客户和效益!创新互联公司为您提供成都网站建设,五站合一网站设计制作,服务好的网站设计公司,成都网站设计、成都网站制作负责任的成都网站制作公司!
高阶函数、柯里化、装饰器、functools、文档字符串
目录
高阶函数...1
内建函数-高阶函数...3
currying柯里化:...4
decorator装饰器:...4
装饰器(无参):...7
带参装饰器:...9
functools:...12
文档字符串:...14
函数:
first class object,函数在python中是一等公民;
函数也是对象,可调用的对象;
函数可以作为普通变量、参数、返回值等;
高阶函数
数学概念,y=g(f(x));
在数学和计算机科学中,高阶函数应当是至少满足下面一个条件的函数:
接受一个或多个函数作为参数;
输出一个函数,如return inc;
计数器:
例:
In [1]: def counter(base): #函数counter()是一个高阶函数
...: def inc(step=1):
...: nonlocal base
...: base += step #赋值前引用,用nonlocal解决
...: return base
...: return inc
...:
In [2]: foo=counter(10)
In [3]: foo1=counter(10)
In [4]: foo==foo1 #foo和foo1不一样,可理解为inc为counter内的标识符,每次counter()运行是不同的对象
Out[4]: False
In [5]: id(foo)
Out[5]: 139964301851096
In [6]: id(foo1)
Out[6]: 139964302182600
In [7]: foo()
Out[7]: 11
In [8]: foo()
Out[8]: 12
In [9]: foo1()
Out[9]: 11
In [10]: foo1()
Out[10]: 12
自定义一个sort函数:
排序问题,参照内建函数sorted,自行实现一个sort函数(不使用内建函数),能够为列表元素排序;
需求:原列表不变(sorted(lst));原列表改变(lst.sort());
思路:
内建函数sorted返回一个新列表,可设置升序降序,可设置一个排序的函数,自定义sort函数也要具备这些功能;
新建一个列表,遍历原列表,和新列表的值依次比较决定如何插入到新列表中;
sorted函数的实现原理,扩展到map、filter函数;
例:
def sort(iterable):
ret = []
for x in iterable:
for i,y in enumerate(ret):
if x > y:
ret.insert(i,x)
break
else:
ret.append(x)
return ret
sort([5,3,2,1,4])
#######################
def sort(iterable,reverse=False):
ret = []
for x in iterable:
for i,y in enumerate(ret):
flag = x < y if reverse else flag = x > y
if flag:
ret.insert(i,x) #找到大的就插入,降序
break
else:
ret.append(x) #小的尾部追加
return ret
sort([5,3,2,1,4],reverse=True)
#######################
def sort(iterable,key=lambda a,b:a
ret = []
for x in iterable:
for i,y in enumerate(ret):
if key(x,y):
ret.insert(i,x)
break
else:
ret.append(x)
return ret
sort([5,3,2,1,4],lambda a,b:a>b)
#########################
sorted(iterable[,key][,reverse])-->new list,排序,返回一个新列表,对一个可迭代对象的所有元素排序,排序规则为key定义的函数,可用reverse指定排序翻转;
例:
In [11]: lst = [3,2,4,5,1]
In [12]: sorted(lst,key=lambda x:6-x) #内置sorted的key可对每个元素进行变化,该变化只在比较时用于临时值,比较后的新列表中的元素仍然是原列表中的元素
Out[12]: [5, 4, 3, 2, 1]
In [13]: lst = ['a',1,2,3,'b']
In [15]: sorted(lst,key=str) #key=str为比较逻辑
Out[15]: [1, 2, 3, 'a', 'b']
filter(function,iterable)-->filter object,generator,过滤数据,过滤可迭代的元素,返回一个迭代器;function是一个具有一个参数的函数,返回bool;
例:
In [16]: filter(lambda x:x%3==0,[1,9,55,150,-3.78,28,123])
Out[16]:
In [17]: list(filter(lambda x:x%3==0,[1,9,55,150,-3.78,28,123])) #过滤出列表中能被3整除的数字;注意function中要有参数x,否则报错
Out[17]: [9, 150, 123]
map(function,*iterables)-->map object,generator,映射,对多个可迭代对象的元素按指定的函数进行映射,返回一个迭代器;
例:
In [18]: map(lambda x:2*x+1,range(5))
Out[18]:
In [20]: list(map(lambda x:2*x+1,range(5))) #可用此方式获取奇数、偶数
Out[20]: [1, 3, 5, 7, 9]
In [21]: dict(map(lambda x:(x%5,x),range(500))) #构造字典,通过key已去重(后面覆盖前面的了)
Out[21]: {0: 495, 1: 496, 2: 497, 3: 498, 4: 499}
指将原来接受2个参数的函数变成新的接受一个参数的过程,新的函数返回一个以原有第二个参数为参数的函数;
z=f(x,y)-->z=f(x)(y),这种形式;
例:
将加法函数柯里化:
In [22]: def add(x,y):
...: return x+y
...:
In [23]: def add(x):
...: def _add(y):
...: return x+y
...: return _add
...:
装饰器的用途:
装饰器是AOP思想的体现,aspect oriented programming面向切面编程;
面向对象往往需要通过继承或组合依赖等方式调用一些功能,这些功能的代码往往可能在多个类中出现,如logger,这样造成代码的重复,增加了耦合,logger的改变影响所有使用它的类或方法;
而AOP在需要的类或方法上切下,前后的切入点可加入增强的功能,让调用者和被调用者解耦;
这是一种不修改原来的业务代码,给程序动态添加功能的技术,如logger函数功能就是对业务函数增加日志的,而业务函数中应把与业务无关的日志功能剥离干净;
装饰器应用场景:
日志、监控、权限、设计、参数检查、路由等;
这些功能与业务功能无关,很多业务都需要公共功能,所以适合独立出来,需要的时候对目标对象增强;
需求:
加法函数,增强其功能,输出被调用过及调用的参数信息;
例:
def add(x,y):
print('call: {},{}+{}'.format(add.__name__,x,y))
return x+y
add(1,2)
注:
此方式完成了需求,但有缺点;
打印语句的耦合太高,与定义的函数紧紧关联;
加法函数属于业务功能,而输出信息是非业务功能代码,不该放在业务函数中,这称为侵入式代码;
##################
def add(x,y):
return x+y
def logger(fn):
print('begin')
x=fn(4,5)
print('end')
return x
print(logger(add))
注:
虽做到了业务功能分离,但fn函数调用传参是个问题;
##################
del add
def add(x,y,*args):
return x+y
def logger(fn,x,y):
print('begin')
ret=fn(x,y)
print('end')
return ret
print(logger(add,4,5))
###################
def add(x,y,z):
return x+y+z
def logger(fn,*args,**kwargs): #可变参数,解决了传参问题
print('begin')
ret=fn(*args,**kwargs) #参数解构
print('end')
return ret
print(logger(add,4,z=5,y=6))
###################
def add(x,y,z):
return x+y+z
def logger(fn): #在上例基础上将logger柯里化
def _logger(*args,**kwargs): #可变参数
print('begin')
ret=fn(*args,**kwargs) #参数解构;闭包(fn为自由变量)
print('end')
return ret
return _logger #返回内部函数的引用
print(logger(add)(4,5,6))
foo=logger(add)
print(foo(4,5,6)) #等价于print(logger(add)(4,5,6))
add=logger(add) #等价于@logger
print(add(4,5,6))
#####################
def logger(fn): #如果此处用两个参数def logger(fn,x),后面使用@logger时会语法错误,解决办法,进一步柯里化
def _logger(*args,**kwargs): #可变参数
print('begin')
ret=fn(*args,**kwargs) #参数解构;此处闭包,正因为闭包原函数在@logger(即add=logger(add))被保留下来;此处如果写为return ret=fn(*args,**kwargs)后面语句将不会执行
print('end')
return ret #若无此行会破坏原函数
return _logger #外层函数应返回内层函数的引用
@logger #等价于add=logger(add);第一个add已指向内层函数_logger一般起名为wrapper包装函数;第二个add(即logger(add))为原函数;@logger要放到被包装函数的上一行
def add(x,y):
return x+y
print(add(4,5)) #此处add不是原函数,而是被包装后的内层函数(即add=logger(add))
注:
外层函数应返回内层函数的引用;
外层函数的参数应为要包装的函数,add=logger(add);
@logger即装饰器的语法;
它是一个函数;
函数作为它的形参;
返回值也是一个函数;
可以使用@function方式,简化调用;
装饰器和高阶函数:
装饰器是高阶函数,但装饰器是对传入函数的功能的装饰(功能增强);
例:
import datetime
import time
def logger(fn):
def wrapper(*args,**kwargs):
print('args={},kwargs={}'.format(args,kwargs))
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
duration = (datetime.datetime.now()) - start
print('function {} took {}s'.format(fn.__name__,duration.total_seconds()))
return ret
return wrapper
@logger
def add(x,y):
print('=========call add===========')
time.sleep(2)
return x+y
print(add(4,y=5))
例:
def copy_properties(src,dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
dst.qualname__ = src.__qualname__
def logger(fn):
def wrapper(*args,**kwargs):
'''This is a wrapper'''
print('begin')
ret = fn(*args,**kwargs)
print('end')
return ret
copy_properties(fn,wrapper)
return wrapper
@logger
def add(x,y):
'''
This is a additional.
return int
x int
y int
'''
ret = x + y
return ret
print(add.__name__,add.__doc__,add.__qualname__,sep='\n')
#######################
def copy_properties(src):
def wrapper(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
dst.qualname__ = src.__qualname__
return dst
return wrapper
def logger(fn):
@copy_properties(fn) #等价于wrapper=copy_properties(fn)(wrapper),出现的wrapper可认为都是logger内的wrapper,copy_properties(fn)即copy_properties内的wrapper
def wrapper(*args,**kwargs):
'''This is a wrapper'''
print('begin')
ret = fn(*args,**kwargs)
print('end')
return ret
#copy_properties(fn,wrapper)
return wrapper
@logger
def add(x,y):
'''
This is a additional.
return int
x int
y int
'''
ret = x + y
return ret
print(add.__name__,add.__doc__,add.__qualname__,sep='\n')
注:
包装函数属性改为被包装函数属性;
调用时用到的是嵌套函数内层wrapper的属性而不是原函数add的属性,在使用copy_properties函数后,可让使用者认为是原函数的属性;
通过copy_properties函数将被包装函数的属性覆盖掉包装函数;
凡是被装饰的函数都要复制这些属性,上例很通用;
可以将复制属性的函数构建成装饰器函数,带参装饰器;
是一个函数;
函数作为它的形参;
返回值是一个不带参的装饰器函数(不一定);
使用@function(参数列表)方式调用;
可以看作在装饰器外层又加了一层函数;
最多也就三层;
需求:
获取函数的执行时长,对长超过阈值的函数记录一下;
例:
import datetime
import time
def logger(t):
def _logger(fn):
def wrapper(*args,**kwargs):
'''This is a wrapper.'''
print('args={},kwargs={}'.format(args,kwargs))
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
duration = (datetime.datetime.now() - start).total_seconds()
if duration > t:
print('function {} took {}s'.format(fn.__name__,duration))
return ret
return wrapper
return _logger
@logger(3) #等价于add=logger(3)(add)
def add(x,y):
print('===============call add================')
time.sleep(4)
return x+y
print(add(4,5))
###########################
import datetime
import time
def copy_properties(src):
def wrapper(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
dst.__qualname__ = src.__qualname__
return dst
return wrapper
def logger(duration):
def _logger(fn):
@copy_properties(fn) #等价于add=copy_properties(add)(dst)
def wrapper(*args,**kwargs):
'''This is a wrapper.'''
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now() - start).total_seconds()
print('so slow') if delta > duration else print('so fast')
return ret
return wrapper
return _logger
@logger(3) #等价于add=logger(3)(add)
def add(x,y):
print('===============call add================')
time.sleep(2)
return x+y
print(add(4,5))
##########################
def logger(fn): #无论logger(add)是什么都返回10,语法允许,在装饰器角度并没有装饰什么,把原函数add给废掉了
return 10
@logger #add=logger(add)
def add(x,y):
return x+y
print(add)
############################
import datetime
import time
def copy_properties(src):
def wrapper(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
dst.__qualname__ = src.__qualname__
return dst
return wrapper
def logger(duration,func=lambda name,duration: print('{} took {}s'.format(name,duration))):
def _logger(fn):
@copy_properties(fn)
def wrapper(*args,**kwargs):
'''This is a wrapper.'''
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now() - start).total_seconds()
if delta > duration:
func(fn.__name__,duration)
return ret
return wrapper
return _logger
@logger(3)
def add(x,y):
print('===============call add================')
time.sleep(4)
return x+y
print(add(4,5))
注:
将记录的功能提取出来,这样就可通过外部提供的函数来灵活的控制输出;
functools.update_wrapper(wrapper,wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)
类似copy_properties功能;
wrapper包装函数,wrapped被包装函数;
元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性,__module__模块名,__name__名称,__qualname__限定名,__doc__文档,__annotations__参数注解;
元组WRAPEER_UPDATES中是要被更新的属性,__dict__属性字典,注意字典要用update方式;
增加一个__wrapped__属性,保留着wrapped函数;
例:
import datetime,time,functools
def logger(duration,func=lambda name,duration: print('{} took {}s'.format(name,duration))):
def _logger(fn):
def wrapper(*args,**kwargs):
'''This is a wrapper.'''
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now() - start).total_seconds()
if delta > duration:
func(fn.__name__,duration)
return ret
functools.update_wrapper(wrapper,fn)
return wrapper
return _logger
@logger(3)
def add(x,y):
print('===============call add================')
time.sleep(4)
return x+y
print(add(4,5),add.__name__,add.__wrapped__,add.__dict__,sep='\n')
#######################
import datetime
import time
import functools
def logger(duration,func=lambda name,duration: print('{} took {}s'.format(name,duration))):
def _logger(fn):
@functools.wraps(fn) #经常用
def wrapper(*args,**kwargs):
'''This is a wrapper.'''
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now() - start).total_seconds()
if delta > duration:
func(fn.__name__,duration)
return ret
return wrapper
return _logger
@logger(3)
def add(x,y):
print('===============call add================')
time.sleep(4)
return x+y
print(add(4,5),add.__name__,add.__wrapped__,add.__dict__,sep='\n')
#######################
import functools
def logger(fn):
@functools.wraps(fn)
def wrapper(*args,**kwargs):
'''This is a wrapper'''
print('begin')
ret = fn(*args,**kwargs)
print('end')
return ret
print('{} {}'.format(id(wrapper),id(fn)))
return wrapper
@logger
def add(x,y):
'''
This is a additional.
return int
x int
y int
'''
ret = x + y
return ret
print(add.__name__,add.__doc__,add.__qualname__,sep='\n')
print('*'*50)
print(id(add.__wrapped__))
print(add(4,5))
print(add.__wrapped__(4,5)) #用包装函数调用,如add(4,5);而不要用被包装函数调用,如add.__wrapped__(4,5)
python的文档:
在函数语句块的第一行,且习惯是多行的文本,所以多使用三引号,约定使用双引号;
惯例是首字母大写,第一行写概述,空一行,第三行写详细描述;
可使用特殊属性__doc__访问这个文档;
例:
In [28]: def add(x,y):
...: '''
...: This is a funtion of addition.
...:
...: return int
...: x int
...: y int
...: '''
...: return x+y
...:
In [29]: print('name={}\ndoc={}'.format(add.__name__,add.__doc__))
name=add
doc=
This is a funtion of addition.
return int
x int
y int
In [30]: help(add)
……
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流