Python函数式编程-高阶函数、匿名函数、装饰器、偏函数
本篇文章我们来介绍下Python函数式编程的知识。最主要的一点,Python中的函数是对象,可以复制给变量!好了,我们来介绍几个Python函数式编程中的要点,包括高阶函数、匿名函数、装饰器、偏函数等等。精彩内容,不容错过!
1、高阶函数
函数本身也可以赋值给变量,即:变量可以指向函数。如果一个变量指向了一个函数,那么,可以通过该变量来调用这个函数。
f = abs f(-10) # 10
既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
def add_abs(x,y,f): return f(x) + f(y) f = abs add_abs(-5,6,f) # 11
接下来,我们介绍几个重点的高阶函数。
map函数
map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。我们之前介绍过了,Iterator是惰性序列,需要通过list()函数让它把返回结果变为list。
def f(x): return x * x r = list(map(f,[1,2,3,4,5,6,7,8,9,10])) r
输出结果为:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
reduce函数
reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算:
def f(x,y): return x + y reduce(f,[1,2,3,4,5]) # 15
filter函数
和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。filter()函数返回的是一个Iterator,也就是一个惰性序列,同样需要通过list()函数让它把返回结果变为list。
def is_odd(n): return n%2==1 list(filter(is_odd,[1,2,3,4,5,6,7,8,9]))
结果为:
[1, 3, 5, 7, 9]
sorted函数
sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序或者按照小写字母顺序进行排序:
print(sorted([36,5,-12,9,-21],key=abs)) print(sorted(['bob', 'about', 'Zoo', 'Credit'],key=str.lower))
结果为:
[5, 9, -12, -21, 36] ['about', 'bob', 'Credit', 'Zoo']
如果你在学习Python的过程中遇见了很多疑问和难题,可以加-q-u-n 227 -435-450里面有软件视频资料免费
2、函数作为返回值
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
我们来实现一个可变参数的求和,如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数:
def lazy_sum(*args): def sum(): res = 0 for n in args: res += n return res return sum f = lazy_sum(1,3,5,7,9) f()
在这个例子中,我们在函数lazy_sum中又定义了函数sum,
并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,
当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,
这种称为“闭包(Closure)”的程序结构拥有极大的威力。
请再注意一点,当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数:
f1 = lazy_sum(1,3,5,7,9) f2 = lazy_sum(1,3,5,7,9) f1 == f2 # False
另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。我们来看一个例子:
def count(): fs = [] for i in range(1,4): def f(): return i * i fs.append(f) return fs f1,f2,f3 = count() print(f1()) print(f2()) print(f3())
输出结果为:
9 9 9
全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:
def count(): fs = [] def sub(j): def f(): return j * j return f for i in range(1,4): fs.append(sub(i)) return fs f1,f2,f3 = count() print(f1()) print(f2()) print(f3()
结果为:
1 4 9
3、匿名函数lambda
当我们在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便。相信大家对于匿名函数一定不陌生,其实就是我们常说的lambda函数:
list(map(lambda x:x * x,[1,2,3,4,5,6,7,8,9])) def build(x,y): return lambda:x * x + y * y reduce(lambda x,y:x + y,[1,2,3,4,5,6,7,8,9])
4、装饰器
在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
本质上,decorator就是一个返回函数的高阶函数。
所以,假设我们要定义一个能够打印当前函数名的decorator:
def log(func): def wrapper(*args,**kwargs): print('call %s()' % func.__name__) return func(*args,**kwargs) return wrapper @log def helloworld(): print('hello world') helloworld()
执行结果为:
call helloworld() hello world
下面的例子中,正是由于wrapper中把 func(args,*kwargs)进行return,因此函数得以执行。
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:
def log(text): def decorator(func): def wrapper(*args,**kwargs): print('%s %s():' % (text,func.__name__)) return func(*args,**kwargs) return wrapper return decorator @log('execute') def helloworld(): print('hello world') helloworld()
上面代码的执行过程相当于helloworld = log('execute')(helloworld)
我们讲了函数也是对象,它有name等属性,但你去看经过decorator装饰之后的函数,它们的name已经从原来的'helloworld'变成了'wrapper':
helloworld.__name__ #'wrapper'
如果需要把原始函数的name等属性复制到wrapper()函数中,使用Python内置的functools.wraps函数,代码如下:
import functools def log(func): @functools.wraps(func) def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper @log def helloworld(): print('hello world') helloworld.__name__
此时的输出就变为了'helloworld'
5、偏函数
一般的函数有许多需要定义的参数,假设我们想要固定其中的某些参数,返回一些新的函数,我们就可以使用functools.partial帮助我们创建一个偏函数,从而使得调用变得简单
import functools int2 = functools.partial(int, base=2) int2('10010') # 18
当然我们也可以穿入一个函数字典:
kw = { 'base': 2 } int('10010', **kw)
当传入的参数没有对应的key时,它默认时作为*args的一部分自动加到左边,因此下面的函数相当于比较max(10,5,6,7),返回10:
max2 = functools.partial(max, 10) max2(5, 6, 7)