高阶函数的定义
返回函数或者参数是函数的函数,就是高阶函数
在Python中函数是一等对象(first class),因为函数也是对象,并且它可以像普通对象一样赋值,作为参数,作为返回值。
>>> def counter(base): 这是一个闭包的结构,也是一个典型的高阶函数,返回值是函数
... def inc(x=1):
... nonlocal base
... base += x
... return base
... return inc
...
>>> inc = counter(3)
>>> inc()
4
>>> inc(4)
9
高阶函数的应用场景
将匿名函数lambda当做参数来传递,会大大减少代码量,提升代码运行速度,清晰代码的阅读
In [6]: def sort(it,cmp=lambda a,b:a<b):
...: ret = []
...: for x in it:
...: for i,v in enumerate(ret):
...: if cmp(x,v):
...: ret.insert(i,x)
...: break
...: else:
...: ret.append(x)
...: return ret
...:
In [7]: sort([1,4,2,3,6,5])
Out[7]: [1, 2, 3, 4, 5, 6]
In [8]: sort([1,4,2,3,6,5],lambda a,b:a>b)
Out[8]: [6, 5, 4, 3, 2, 1]
使用函数作为参数,并且返回值也是函数的场景,也叫做装饰器
实现一个获取函数返回时间的高阶函数
In [3]: def logger(fn): 函数作为返回值,封装了fn
...: def wrap(*args,**kwargs): 可变参数
...: start = datetime.datetime.now() 函数的开始时间
...: ret = fn(*args,**kwargs) 参数解构
...: end = datetime.datetime.now() 函数的结束时间
...: print('call {} took {}'.format(fn.__name__,end-start))
...: return ret
...: return wrap
...:
In [4]: def add(x,y):
...: return x + y
...:
In [5]: loged_add = logger(add)
In [6]: loged_add(3,5) 将add函数的执行时间打印出来,返回值也打印出来
call add took 0:00:00.000009
Out[6]: 8
函数作为返回值:通常是用在闭包的时候,需要封装一些变量
函数作为参数:通常用于大多数逻辑固定,少部分逻辑不固定的场景
函数作为参数,返回值也是函数:通常用于作为参数函数执行前后需要一些额外的操作
装饰器
参数是一个函数,返回值是一个函数的函数,就可以作为装饰器
只有使用 @ 形式的函数才是一个装饰器
@logger
def sleeps(x):
time.sleep(x)
使用 functools.wraps 函数来完善使用装饰器带来的副作用
当使用装饰器后,被装饰的函数的 __name__和__doc__方法都是变成装饰器中的二级函数
print('__name__ is {}, __doc__ is {}'.format(sleeps.__name__,sleeps.__doc__))
__name__ is 'wrap', __doc__ is 'None'
在有些时候是需要调用到函数本身的名称的, 因此使用 functools.wraps 将被装饰的函数变回原来的样子
import datetime
import time
import functools
def logger(fn):
@functools.wraps(fn) 在这里重新装饰下装饰器的二级函数
def wrap(*args, **kwargs):
start = datetime.datetime.now()
ret = fn(*args, **kwargs)
end = datetime.datetime.now()
print('call {} took {}'.format(fn.__name__,end-start))
return ret
return wrap
@logger
def sleeps(x):
time.sleep(x)
print('__name__ is {}, __doc__ is {}'.format(sleeps.__name__,sleeps.__doc__)) 获取的就是被装饰函数本身的信息
functools.wraps 装饰器的原型
def wraps(src):
def _copy(dst):
dst.__name__ = src.__name__
dst.__doc__ = src.__doc__
return dst
return _copy
带参数的装饰器
带参数的装饰器: 一个函数,返回一个不带参数的装饰器,那么这个函数就是一个带参数的装饰器
通俗点来说,就是在装饰器外面包裹一个函数,也可以说是,一个带参数的函数封装了一个装饰器,就变成了一个带参数的装饰器
装饰器最多是两层嵌套
import datetime
import functools
import time
def logger(s):
def _logger(fn):
@functools.wraps(fn)
def wrap(*args, **kwargs):
start = datetime.datetime.now()
ret = fn(*args, **kwargs)
end = datetime.datetime.now()
if (end-start).total_seconds() > s:
print('{} call {} took'.format(fn.__name__, end-start))
return ret
return wrap
return _logger
@logger(2)
def sleep(x):
time.sleep(x)
sleep(1) 不会返回任何结果
sleep(3) 返回结果集
lambda和装饰器结合使用,直接修改上面的装饰器
def logger(s, p=lambda name,t: print('{} call {} took'.format(name, t))):
def _logger(fn):
@functools.wraps(fn)
def wrap(*args, **kwargs):
start = datetime.datetime.now()
ret = fn(*args, **kwargs)
end = datetime.datetime.now()
if (end-start).total_seconds() > s:
p(fn.__name__, end-start) 调用lambda函数
return ret
return wrap
return _logger
操作函数的方法
给自定义的对象添加帮助信息
def fn():
''' this is fn '''
使用 doc 也是看帮助信息
fn.__doc__
使用 name 获取对象名称
fn.__name__
使用 dir 获取对象所有的方法
dir(fn)