高阶函数:函数可以作为变量传来传去,即自由传递,当一个函数接受另一个函数作为参数的时候,那个时候这种函数我们就把它称之为高阶函数。
1.高阶函数的定义
例1:
讲装饰器的时候,time_master()将myfunc()函数作为参数使用,可以说time_master()是一个高阶函数。
在已学过的函数中,map(),filter()是高阶函数,min(),max(),salty()也可以算是高阶函数,因为它们有一个key参数,接受的正是一个函数对象。
import time
def time_master(func):
def call_func():
print("开始运行程序...")
start=time.time()
func()
stop=time.time()
print("结束程序运行...")
print(f"一共耗费了{(stop-start):.2f}秒。")
return call_func
def myfunc():
time.sleep(2)
print("I love FishC.")
myfunc=time_master(myfunc)
myfunc()
2.functools – 高阶函数
高阶函数是函数式编程的灵魂所在,所以python专程搞了一个模块叫做functools,它包含了非常多实用的高阶函数以及装饰器。
例2:reduce函数
functools.reduce(function, iterable[, initializer])
将 iterable 可迭代对象中的元素依次传递到第一个参数指定的函数中,最终返回累积的结果。
例如, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) 是计算 ((((1+2)+3)+4)+5) 的值。左边参数 x 是累加值,而右边的参数 y 则是来自 iterable 的更新值。
>>> def add(x,y):
return x+y
>>> import functools
>>> functools.reduce(add,[1,2,3,4,5])
15
>>> add(add(add(add(1,2),3),4),5) # 等价于functools.reduce(add,[1,2,3,4,5])
15
>>>
>>> functools.reduce(lambda x,y:x*y, range(1,11)) # 10的阶乘
3628800
>>>
3.偏函数
偏函数是指对指定的函数进行二次包装,通常是将现有的函数部分参数预先给绑定,从而得到一个新的函数,该函数我们称之为偏函数。
偏函数的作用就是将一个函数的多个参数给拆分,多次进行传递的样子。
例3:partial函数
functools.partial(func, /, *args, **keywords)
返回一个偏函数,当被调用时,其行为类似于 func 函数附带位置参数 args 和关键字参数 keywords 被调用。
>>> import functools
>>> square=functools.partial(pow,exp=2)
>>> square(2)
4
>>> square(3)
9
>>> cube=functools.partial(pow,exp=3)
>>> cube(2)
8
>>> cube(3)
27
4.@wraps装饰器
例4:普通闭包函数
装饰器的例子
import time
def time_master(func):
def call_func():
print("开始运行程序...")
start=time.time()
func()
stop=time.time()
print("结束程序运行...")
print(f"一共耗费了{(stop-start):.2f}秒。")
return call_func
@time_master
def myfunc():
time.sleep(2)
print("I love FishC.")
myfunc()
结果:
>>>
================ RESTART: E:\xiaojiayu code\051讲:高阶函数(XII).py ================
开始运行程序...
I love FishC.
结束程序运行...
一共耗费了2.08秒。
>>>
>>> myfunc.__name__ #name的名字是call_func,而不是myfunc
'call_func'
例5:@wraps装饰器
@functools.wraps(func) 真正的函数是通过func这个函数传入,我们把真正的函数给作为参数传给这个wraps。
import time
import functools
def time_master(func):
@functools.wraps(func)
def call_func():
print("开始运行程序...")
start=time.time()
func()
stop=time.time()
print("结束程序运行...")
print(f"一共耗费了{(stop-start):.2f}秒。")
return call_func
def myfunc():
time.sleep(2)
print("I love FishC.")
myfunc=time_master(myfunc)
myfunc()
结果:没有副作用, myfunc. __ name__ 的名字变为myfunc
>>>
================ RESTART: E:\xiaojiayu code\051讲:高阶函数(XII).py ================
开始运行程序...
I love FishC.
结束程序运行...
一共耗费了2.02秒。
>>>
>>> myfunc.__name__ # name的名字变为myfunc
'myfunc'
>>>
课后题:
1.偏函数的实现原理是?
答:闭包。
解析:
偏函数是对指定函数的二次包装,通常是将现有函数的部分参数预先绑定,从而得到一个新的函数,该函数就称为偏函数。
下面代码是偏函数的实现原理(只需要知道它是闭包原理,下面代码大致看得懂就可以,不要求掌握):
def partial(func, /, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = {**keywords, **fkeywords}
return func(*args, *fargs, **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
2.请将下面闭包代码改用偏函数来实现?
>>> def power(exp):
... def exp_of(base):
... return base ** exp
... return exp_of
...
>>> square = power(2)
>>> cube = power(3)
>>> square
.exp_of at 0x000001CF6A1FAF70>
>>> square(2)
4
>>> square(5)
25
>>> cube(2)
8
>>> cube(5)
125
答:
>>> square = functools.partial(pow, exp=2)
>>> square(2)
4
>>> square(3)
9
>>> cube = functools.partial(pow, exp=3)
>>> cube(2)
8
>>> cube(3)
27
解析:pow() 函数需要两个参数,这里我们通过偏函数先指定其中的 exp 参数,然后第二个参数在实际调用的时候再进行传递,这就是偏函数的 “延迟” 效果。
3.请使用偏函数,基于 print() 函数打造一个新的 pr() 函数,使其实现效果如下:
>>> pr("I", "love", "FishC.")
I@love@FishC.#
答:
>>> import functools
>>> pr = functools.partial(print, sep='@', end='#')
>>> pr("I", "love", "FishC.")
I@love@FishC.#
4.请修改下面代码,使得执行 test.name 语句后,得到的结果是 ‘test’,而非 ‘call_func’。
>>> def myfunc(func):
... def call_func():
... func()
... return call_func
...
>>> @myfunc
... def test():
... print("test~")
...
>>> test()
test~
>>> test.__name__
'call_func'
答:
>>> import functools
>>> def myfunc(func):
... @functools.wraps(func)
... def call_func():
... func()
... return call_func
...
>>> @myfunc
... def test():
... print("test~")
...
>>> test()
test~
>>> test.__name__
'test'
题目来自小甲鱼python函数(XI)