函数式编程简介
函数式:functional,一种编程范式,与函数是完全不同的东西。
高阶函数简介
高阶函数:能接收函数做参数的函数。
1.变量可以指向函数,函数名就是指向函数的变量。
1 | f = abs |
2.函数的参数可以接收变量,所以一个函数可以接收另一个函数作为参数。
1 | import math |
Python中返回函数
Python的函数不但可以返回int、str、list、dict等数据类型,还可以返回函数。注意区分返回函数和返回值,
1 | def myabs(): |
返回函数可以把一些计算延迟执行。例如,如果定义一个普通的求和函数,
1 | def calc_sum(lst): |
由于可以返回函数,在后续代码里就可以决定到底要不要调用该函数。
闭包
在函数内部定义的函数和外部定义的函数是一样的,只是无法被外部访问。
例如,将g的定义移入函数f内部,防止其他代码调用g,
1 | def f(): |
但是,考察如下calc_sum函数,
1 | def calc_sum(lst): |
注意:发现没法把lazy_sum移到calc_sum的外部,因为它引用了calc_sum的参数lst。
像这种内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)。
闭包的特点是返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。举例如下,
1 | # 希望一次返回3个函数,分别计算1x1,2x2,3x3: |
第一步:执行代码“f1, f2, f3 = count()”赋值号的右边,调用count()函数,开始执行count()函数的内部命令
第二步:创建一个局部变量fs
第三步:开始执行for循环遍历列表[1,2,3],i赋值1,然后执行fs.append(f)代码,得到[f],注意这时候并不需要执行函数f(),因为没有调用
第四步:继续执行for循环遍历列表[1,2,3],i赋值2,然后执行fs.append(f)代码,得到[f,f],注意这时候并不需要执行函数f(),因为没有调用
第五步:继续执行for循环遍历列表[1,2,3],i赋值3,然后执行fs.append(f)代码,得到[f,f,f],注意这时候并不需要执行函数f(),因为没有调用
第六步:执行代码“f1, f2, f3 = count()”赋值号的左边,将fs(即[f,f,f])赋值给f1,f2,f3,使用到了高级序列赋值语句模式
第七步:执行print (f1(),f2(),f3())代码
第八步:调用函数f(),执行return ii,此时的i的地址值指向的是外层函数中i的地址,i的值应为for之前的for循环重新赋值为3,所以return 33
因此,返回函数不要引用任何循环变量,或者后续会发生变化的变量。
匿名函数
在Python中,对匿名函数提供了有限支持。以map()函数为例,计算 f(x)=x2时,除了定义一个f(x)的函数外,还可以直接传入匿名函数
1 | map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]) # [1, 4, 9, 16, 25, 36, 49, 64, 81] |
通过对比可以看出,匿名函数lambda x: x * x实际上就是,
1 | def f(x): |
关键字lambda表示匿名函数,冒号前面的x表示函数参数。
匿名函数只能有一个表达式,不写return,返回值就是该表达式的结果。
使用匿名函数,可以不必定义函数名,直接创建一个函数对象,可以简化代码,
1 | sorted([1, 3, 9, 5, 0], lambda x,y: -cmp(x,y)) # [9, 5, 3, 1, 0] |
返回函数的时候,也可以返回匿名函数,
1 | myabs = lambda x: -x if x < 0 else x |
偏函数
当一个函数有很多参数时,调用者就需要提供多个参数。如果减少参数个数,就可以简化调用者的负担。
比如,int()函数可以把字符串转换为整数,当仅传入字符串时,int()函数默认按十进制转换,
1 | print(int('12345')) # 12345 |
但int()函数还提供额外的base参数,默认值为10。如果传入base参数,就可以做N进制的转换,
1 | print(int('12345', base=8)) # 5349 |
假设要转换大量的二进制字符串,每次都传入int(x, base=2)非常麻烦,于是,可以定义一个int2()的函数,默认把base=2传进去,
1 | def int2(x, base=2): |
functools.partial用来创建偏函数,不需要自己定义int2(),可以直接使用下面的代码创建一个新的函数int2,
1 | import functools |
所以,functools.partial可以把一个参数多的函数变成一个参数少的新函数,少的参数需要在创建时指定默认值。