Python学习(七)函数式编程
Chapter 4 函数式编程
函数是代码的一种封装形式,通过一层层的调用,可以将复杂任务拆解。这种分解的方式可称为面向过程的程序设计,而函数是面向过程编程(Procedure Oriented Programming)的基本单元。
函数式编程(Functional Programming)可以归为是面向过程编程,但思想更为抽象,接近数学计算。
我们知道汇编语言(Assembly Language)是面向机器的程序设计语言,属于低级语言,直接控制CPU运行。而高级语言更接近数学语言和自然语言,如C/C++/Python。对于高级编程语言,越低级的抽象程度低,越贴近计算机,执行效率高,如C语言;越高级的语言抽象程度高,越贴近计算,执行效率低,如Lisp语言。
函数式编程就是抽象程度很高的编程范式。
(1)纯函数:用纯粹的函数式编程语言编写的函数是没有变量的,输入输出确定,这种纯函数被称作没有副作用。
(2)带变量的函数:由于函数内部变量状态不确定,同样输入可能有不同输出。这种函数则是有副作用。
此外,函数式编程可以把函数本身作为参数传入另一个函数。
Python允许使用变量,不是纯函数式编程语言,但是对部分函数式编程提供支持。
4.1 高阶函数
高阶函数(Higher-order function),在数学上又称为算子或泛函。高阶函数可以对其他函数进行操作,接受函数作为参数或作为输出返回。
(1)变量可以指向函数
以Python内置的求绝对值的函数abs()
为例
函数调用
>>> abs(-10)
10
函数本身
>>> abs
<built-in function abs>
函数调用的结果赋值给变量
>>> x = abs(-10)
>>> x
10
函数本身赋值给变量
>>> f = abs
>>> f
<built-in function abs>
因此,函数本身也可以赋值给变量,即变量可以指向函数。
这样可以通过变量来调用函数。
>>> f = abs
>>> f(-10)
10
变量f
现在已经指向了abs
函数本身。直接调用abs()
函数和调用变量f()
完全相同。
(2)函数名也是变量
函数名可以理解成指向函数的变量。对于abs()
这个函数,可以把函数名abs
看成变量,它指向一个可以计算绝对值的函数。
如果变量abs
指向其他对象:
>>> abs = 10
>>> abs(-10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
把abs
指向10后,就无法通过abs(-10)调用该函数了。因为abs这个变量已经不指向求绝对值函数而是指向一个整数10。
因此实际代码最好不要将变量名设为内置函数名,避免指向混乱的情况。
ps:abs
函数实际上是定义在import builtins
模块中的,所以要让修改abs变量的指向在其它模块也生效,要用import builtins; builtins.abs = 10
(3)函数作为参数传入
上面解释了变量可以指向函数,加上函数的参数能够接受变量,那么一个函数接收另一个函数作为参数,这种函数就成为高阶函数。
以下面一个高阶函数作为例子:
def add(x, y, f):
return f(x) + f(y)
这里函数add
包含3个参数,x, y, f。返回的是以x为自变量的函数 f(x) 与以y为自变量的函数f(y)的和。接受函数f作为参数和函数f作为输出返回,因此为高阶函数。
调用
add(-5, 6, abs)
相当于参数x
,y
和f
分别接收-5
,6
和abs
x = -5
y = 6
f = abs
f(x) + f(y) ==> abs(-5) + abs(6) ==> 11
return 11
(4)函数作为返回值返回
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
可变参数的求和,定义求和函数:
def calc_sum(*args):
ax = 0
for n in args:
ax = ax + n
return ax
但是如果不需要对输入的参数立马进行求和,在后面的代码中需要再计算的话,可以不返回求和的结果而是返回求和的函数:
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
当调用lazy_sum()
时,返回的不是求和结果,而是求和函数:
>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function lazy_sum.<locals>.sum at 0x101c6ed90>
调用函数f时,才真正计算求和的结果:
>>> f()
25
当调用lazy_sum()
时,每次调用都会返回一个新的函数,即使传入相同的参数:
>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2
False
f1()
和f2()
的调用结果互不影响,即每次调用的结果互不影响。