何为函数式编程,是一种编程范式,函数-function 函数式-functional,函数式编程支持高阶函数。Python支持的函数式编程有以下特点

  1. 不是纯函数式编程:允许有变量
  2. 支持高阶函数:函数也可以作为变量传入
  3. 支持闭包:有了闭包就能返回函数
  4. 有限度的支持匿名函数
  5. 函数名其实是指向函数的变量

1.高阶函数

可以接受函数的函数称之为高阶函数

>>>def add(x,y,f):
    return f(x)+f(y)
>>>add(-4,4,abs)
8

2.内置函数

(1)map()函数

map()是 Python 内置的高阶函数,它接收一个函数 f 和一个 list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回。

例如,对于list [1, 2, 3, 4, 5, 6, 7, 8, 9]

如果希望把list的每个元素都作平方,就可以用map()函数:

因此,我们只需要传入函数f(x)=x*x,就可以利用map()函数完成这个计算:

def f(x):
    return x*x
print map(f,range(1,10))

输出结果为:

[1,4,9,16,25,36,49,64,81]

在这里map()不改变原来list的值,而是返回一个新的list。

(2)reduce()函数

reduce()函数也是Python内置的一个高阶函数。reduce()函数接收的参数和 map()类似,一个函数 f,一个list,但行为和 map()不同,reduce()传入的函数 f 必须接收两个参数,reduce()对list的每个元素反复调用函数f,并返回最终结果值。

例如,编写一个f函数,接收x和y,返回x和y的和:

def f(x,y):
    return x+y
先调用 reduce(f, [1, 3, 5, 7, 9])时,reduce函数将做如下计算:计算头两个元素:f(1, 3),结果为4;
再把结果和第3个元素计算:f(4, 5),结果为9;
再把结果和第4个元素计算:f(9, 7),结果为16;
再把结果和第5个元素计算:f(16, 9),结果为25;
由于没有更多的元素了,计算结束,返回结果25。

上述计算实际上是对 list 的所有元素求和。虽然Python内置了求和函数sum(),但是,利用reduce()求和也很简单。

reduce()还可以接收第3个可选参数,作为计算的初始值。如果把初始值设为100,计算:

reduce(f, [1, 3, 5, 7, 9], 100)

结果将变为125,因为第一轮计算是:

计算初始值和第一个元素:f(100, 1),结果为101。

(3)filter()函数

filter()函数是 Python 内置的另一个有用的高阶函数,filter()函数接收一个函数 f 和一个list,这个函数 f 的作用是对每个元素进行判断,返回 True或 False,filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list。

例如,要从一个list [1, 4, 6, 7, 9, 12, 17]中删除偶数,保留奇数,首先,要编写一个判断奇数的函数:

def is_odd(x):
    return x % 2 == 1

然后,利用filter()过滤掉偶数:

filter(is_odd, [1, 4, 6, 7, 9, 12, 17])

结果:[1, 7, 9, 17]

(4)strip()函数

s.strip(rm)删除s字符串中开头,结尾处的rm序列的字符,当rm为空时,默认删除空白符

a = '     123\t'
a.strip()

结果:’123’

(5)自定义排序函数

Python内置的sorted()函数可以对list进行排序,但由于sorted是一个高阶函数可以接受比较函数来实现自定义排序,传入两个待比较的元素x,y,如果x应该排在y的前面返回-1,否则返回1,相等返回0.

def reverse(x,y):
    if x>y:
        return -1
    if x<y:
        return 1
    return 0

这样调用sorted并传入reverse即实现倒序排序:

>>>soreted([32,3,24,6],reverse)
[32,24,6,3]

(6)返回函数

Python的函数不仅可以返回int,str,list,dict等,也可以返回函数。例如定义一个函数,我们让它返回一个函数g:

def f():
    print 'call f()'
    def g():
        print 'call g()'
    return g

我们在函数内部有定义了一个函数g,由于函数g也是一个对象,函数名g就是指向函数g的变量,所以,f返回了变量g,也就是函数g本身。调用函数:

>>>x=f()
call f()
>>>x()
call g()

由上我们可以看出返回函数可以把一些计算延迟,这样我们就可以在得到函数后再判断是否执行计算。
(7)闭包

def f(lst):
    def g()
        print lst
    return g

像这种内层函数引用了外层函数的变量,然后返回内层函数的情况,称之为闭包。闭包的特点就是还引用了外层的局部变量,所以,要正确使用闭包,就要保证引用的局部变量在函数返回后不发生变化,因此返回函数不要引用任何循环变量,或则后续会发生变化的变量。Python支持一个叫做函数闭包的特性,用人话来讲就是,嵌套定义在非全局作用域里面的函数能够记住它在被定义的时候它所处的封闭命名空间。这能够通过查看函数的func_closure属性得出结论,这个属性里面包含封闭作用域里面的值(只会包含被捕捉到的值,比如x,如果在outer里面还定义了其他的值,封闭作用域里面是不会有的)

(8)匿名函数

匿名函数有个限制,就是只能有一个表达式,不写return,返回值就是该表达式的结果。

使用匿名函数,可以不必定义函数名,直接创建一个函数对象,很多时候可以简化代码:

>>> myabs = lambda x: -x if x < 0 else x 
>>> myabs(-1)
1
>>> myabs(1)
1

3.装饰器

(1)装饰器的来源

def outer(some_func):
     def inner():
         print "before some_func"
         ret = some_func() # 1
         return ret + 1
     return inner
def foo():
     return 1
decorated = outer(foo) # 2
decorated()
before some_func

仔细看看上面这个装饰器的例子。们定义了一个函数outer,它只有一个some_func的参数,在他里面我们定义了一个嵌套的函数inner。inner会打印一串字符串,然后调用some_func,在#1处得到它的返回值。在outer每次调用的时候some_func的值可能会不一样,但是不管some_func的之如何,我们都会调用它。最后,inner返回some_func() + 1的值 – 我们通过调用在#2处存储在变量decorated里面的函数能够看到被打印出来的字符串以及返回值2,而不是期望中调用函数foo得到的返回值1。如果我们用foo=outer(foo)这样我们就用新的foo替代了原来的旧foo,这样我们就完成了对原来函数的更新。

(2) 使用 @ 标识符将装饰器应用到函数

Python2.4支持使用标识符@将装饰器应用在函数上,只需要在函数的定义前加上@和装饰器的名称。在上一节的例子里我们是将原本的方法用装饰后的方法代替:

@outer
 def foo():
    return 1

(3)*args 和**kwargs

我们完成了一个装饰器,但是其只能运用在一类具体的方法上,如果我们想能运用在任何的方法上,这时我们就要用到*。

def one(*args):
     print args # 1
one()
()
one(1, 2, 3)
(1, 2, 3)
def two(x, y, *args): # 2
     print x, y, args
two('a', 'b', 'c')
a b ('c',)

Python允许我们制定一些参数并且通过args捕获其他所有未被捕获的参数,并将其保存在一个tuple列表中。

**代表着键值对的餐宿字典,和*所代表的意义相差无几,如:

def foo(**kwargs):
     print kwargs
foo()
{}
foo(x=1, y=2)
{'y': 2, 'x': 1}

当我们定义一个函数的时候,我们能够用**kwargs来表明,所有未被捕获的关键字参数都应该存储在kwargs的字典中。
(3)更通用的装饰器

有了这招新的技能,我们随随便便就可以写一个能够记录下传递给函数参数的装饰器了。先来个简单地把日志输出到界面的例子:

def logger(func):
     def inner(*args, **kwargs): #1
         print "Arguments were: %s, %s" % (args, kwargs)
         return func(*args, **kwargs) #2
     return inner

请注意我们的函数inner,它能够接受任意数量和类型的参数并把它们传递给被包装的方法,这让我们能够用这个装饰器来装饰任何方法。

@logger
 def foo1(x, y=1):
     return x * y
@logger
 def foo2():
     return 2
foo1(5, 4)
Arguments were: (5, 4), {}
20
foo1(1)
Arguments were: (1,), {}
1
foo2()
Arguments were: (), {}
2