函数是python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。
而函数式编程,虽然也可以归结到面向过程的程序设计,但其思想更接近数学计算。
函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个参数,还允许返回一个函数。
Python对含税式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。
1.高阶函数
高阶函数(Higher-order function)。
①变量可以指向函数
以Python内置的求绝对值的函数abs(为例,调用该函数用以下代码:
abs(-10)
但是如果只写abs就会报错。可见,abs(-10)是函数调用,而abs是函数本身。要获得函数调用结果,我们可以把结果赋值给变量:
x = abs(-10)
但是,如果吧函数本身赋值给变量呢?
f = abs
结论:函数本身也可以赋值给变量,即:变量可以指向函数。
如果一个变量指向了一个函数,那么,可否通过该变量来调用这个函数?用代码验证一下:
f = abs
x = f(-10) // x = 10
成功!说明变量f现在已经指向了abs函数本身。直接调用abs()函数和调用变量f()完全相同。
②函数名也是变量
函数名其实就是指向函数的变量!对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数!
③传入函数
既然变量可以指向函数,函数的参数能接收变量,那么另一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
一个最简单的高阶函数:
def add(x, y, f):
return f(x) + f(y)
2.map/reduce
①map
我们先看map。map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
举例说明,比如我们有一个函数f(x) = x^2,要把这个函数作用在一个list[1,2,4,5,3,6,7,8]上,就可以用map实现:
def f(x):
return x * x
r = map(f, [1,2,3,4,5,6,7,8,9])
//list(r) = [1,4,9,16,25,36,49,64,81]
map()传入的第一个参数是f,即函数对象本身。由于结果r是一个Iterator,Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list.
map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)= x^2,还可以计算任意复杂的函数,比如,把这个list所有数字转化为字符串。
②reduce
再看reduce的用法。reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
3.filter
Python内建的filter()函数用于过滤序列。
和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
例如,在一个list中,删掉偶数,只保留奇数,可以这么写:
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1,2,4,5,6,9,10,15]))
#结果:[1,5,9,15]
把一个序列中的空字符串删掉,可以这么写:
def not_empty(s):
return s and s.strip()
list(filter(not_empty, ['A', '', 'B', None, 'C', ' ']))
#结果:['A', 'B', 'C']
可见用filter()这个高阶函数,关键在于正确实现一个“筛选”函数。
注意到filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list。