Python学习系列:函数式编程

编程范式

常见的编程方式有命令式编程,函数式编程,逻辑式编程。其中常见的面向对象编程就是一种命令式编程。

命令式编程是面向计算机硬件的抽象,有变量、赋值语句、表达式和控制语句等。一句话,命令式程序就是一个冯诺依曼机的指令序列。

而函数式编程是面向数学的抽象,将计算描述为一种表达式求值,一句话,函数式程序就是一个表达式。

函数式编程的本质

函数式编程中的函数这个术语不是指计算机中的函数,而是指数学中的函数,即自变量的映射。也就是说一个函数的值仅决定于函数参数的值,不依赖于其他状态。比如sqrt(x)函数计算x的平方根,只要x的值不变,不论什么时候调用,调用几次,值都是不变的。

在函数式语言中,函数作为一等公民,可以在任何地方定义,在函数内或函数外,可以作为函数的参数和返回值,可以对函数进行组合。

纯函数式编程语言中,变量的值是不可以改变的,不允许像命令式编程语言中那样,多次对一个变量进行赋值。当然了,像python,Java等仅仅用到函数式编程中的一些思想,并不是纯函数式编程,你改不改变量的值都是无关系了。。

函数式编程的好处

由于命令式编程语言也可以通过类似函数指针的方式来实现高阶函数,函数式的最主要的好处主要是不可变性带来的。没有可变的状态,函数就是引用透明的和没有副作用的

一个好处是,函数即不依赖于外部的状态也不修改外部的装态,函数调用的结果不依赖于调用的时间和位置,这样写的代码容易进行推理,不容易出错。这使得单元测试和调试都更容易。

不变性带来的另一个好处是:由于多个线程之间不共享状态,不会造成资源争用,也就不需要用锁来保护可变状态,也就不会出现死锁,这样可以更好地并发起来。

还有个好处是,由于函数式语言是面向数学的抽象,更接近于人的语言,而不是机器语言,代码会比较简洁,也更容易被理解。

函数式语言的一些特性:

  • 高阶函数
  • 偏应用函数
  • 科里化
  • 闭包

Python中函数式编程的应用

高阶函数

什么是高阶函数?如果一个函数可以接受另一个函数作为参数,则此函数称为高阶函数。
下面介绍python中的一些高阶函数的应用。

map/reduce

map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。

如果有一个函数f(x) = x * x,把这个函数作用于一个list,通过map()函数实现如下:

#定义一个函数,求一个数的平方
def f(x):
    return x * x

#调用map函数
list = [1, 2, 3, 4]
r = map(f, list) #f函数作用于list中的每个元素,返回一个Iterator对象
list(r) #将Iterator对象通过list函数返回一个list。

reduce函数是把一个函数作用于一个序列上,reduce也必须接受两个参数,reduce会把结果继续和序列的下一个元素进行累积计算。例子如下:

from functoools import reduce
def add(x, y):
    return x + y
value = reduce(add, [1, 3, 5, 7, 9]) #value的值为25。
#上述reduce表示大致如下:
#value = add(add(add(add(1, 3), 5), 7), 9)
filter

和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,根据返回值是True还是False来决定 是保留还是丢弃该元素。例子如下:

#在一个list中,删除偶数,只保留奇数
def is_odd(n):
    return n % 2 == 1

list = [1, 2, 4, 5, 6, 9, 10, 15]
list(filter(is_odd, list))#返回的结果为[1, 5, 9, 15]
sorted

sorted函数是python的内嵌函数,可以对list进行排序。使用例子如下:

#直接排序
sorted([36, 5, -12, 9, -21])#返回值为[-21, -12, 5, 9, 36]

#sorted()函数是个高阶函数,可以接收一个key函数来实现自定义排序
#按绝对值排序
sorted([36, 5, -12, 9, -21], key = abs)#返回值为[5, 9, -12, -21, 36]

#也可以传递一个reverse参数进行反向排序
sorted([36, 5, -12, 9, -21], key = abs, reverse= True)#返回值为[36, -21, -12, 9, 5]

返回函数

高阶函数不仅可以将函数作为参数传入,同时将参数作为返回值。

实现一个可变参数的求和:

#正常的写法
def calc_sum(*args):
    sum = 0
    for item in args:
        sum = sum + item
    return sum
#调用
calc_sum(1, 2, 3, 4)#返回值为10

#当传递参数后,不立即求和,可以先将求和的函数返回
def lazy_sum(*args):
    def sum():
        ax = 0
        for item in args: #内部的函数可以引用外部函数的局部变量,涉及到闭包的概念
            ax = ax + item
        return ax
    return sum

#调用时返回一个函数,当真正调用时在返回结果
f = lazy_sum(1, 2, 3, 4) #f是一个函数,还不是最终的结果
#返回结果
f() #真正调用时,返回结果为10

匿名函数

关键字lambda表示匿名函数,lambda表达式在Java8中也正是引入了,写起来非常简洁。。

当使用map()函数计算f(x) = x * x时,除了可以直接传入一个函数外,也可以传入一个匿名函数,例子如下:

#传入一个函数
def f(x):
    return x * x
list(map(f, [1, 2, 3, 4]))#结果为[1, 4, 9, 16]

#传递一个匿名函数的形式
list(map(lambda x: x * x, [1, 2, 3, 4]))#结果为[1, 4, 9, 16]

装饰器

对于一个函数,如果想在调用函数的前后自动增加一些新的功能,但又不希望修改此函数的定义。这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。例子如下:

def log(func):
    def wrapper(*args, **kw):
        print('call %s():' %(func.__name__))
        return func(*args, **kw)
    return wrapper

@log
def now():
    print('hello, xinyang')

#调用now(), 把@log放到now()函数定义处,相当于执行了now = log(now),对变量now进行了重新的赋值。
now() #返回值为 call now(): hello, xinyang

偏函数

Python的functools模块提供了很多的功能,其中偏函数就是其中提供的一种功能。偏函数主要实现的功能是:修改一个函数中默认参数的值。例子如下:

int('12345') #返回值为:12345
#int()中还提供了默认的参数base,默认值为10。
int('12345', base = 8) #将'12345'中的数是8进制的,转为整数为:5349
#通过调用functools.partial创建一个偏函数,修改base的值。
#如果想将base的默认值修改为2,正常情况下定义如下:
def int2(x, base = 2):
    return int(x, base)

#借助于functools.partial
import functools
int2 = functools.partial(int, base = 2)
int('1000000') #值为64

你可能感兴趣的:(Python学习系列)