Python函数式编程

Posted by Csming on 2017-04-15

函数式编程(请注意多了一个“式”字)——Functional Programming,虽然也可以归结到面向过程的程序设计,但其思想更接近数学计算


高阶函数

  • 变量可指向函数
1
2
3
a = abs(-10)
print(a)
# 10

1
2
3
f = abs
print(f(-10))
# 10
  • 函数名也是变量
    函数名其实就是指向函数的变量!对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数

  • 传入函数
    变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数

1
2
def add(x, y, f):
return f(x) + f(y)

map/reduce

  • map()函数接受两个参数,一个是函数,一个是Iterable;map将传入的函数一次作用到序列的每个元素;并将结果作为新的Iterator返回;
1
2
3
4
5
6
def f(x):
return x * x

r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(r))
# [1, 4, 9, 16, 25, 36, 49, 64, 81]

map()传入的第一个参数是f,即函数对象本身;由于结果r是一个Iterator,Iterator是惰性序列,需要通过list()函数让它把整个序列都计算出来并返回一个list

map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=x2,还可以计算任意复杂的函数,比如,把这个list所有数字转为字符串

1
2
list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
['1', '2', '3', '4', '5', '6', '7', '8', '9']
  • reduce
    reduce把一个函数作用在一个序列上,这个函数接受两个参数,reduce把结果继续和序列的下一个元素做累积计算;

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

1
2
3
4
5
6
7
from functools import reduce
def add(x, y):
return x + y

print(reduce(add, [1, 3, 5, 7, 9]))

# 25
1
2
3
4
5
6
7
8
9
from functools import reduce
def fn(x, y):
return x * 10 + y

def char2num(s):
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]

print(reduce(fn, map(char2num, '13579')))
13579

可改写为:

1
2
3
4
5
6
7
8
from functools import reduce

def str2int(s):
def fn(x, y):
return x * 10 + y
def char2num(s):
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
return reduce(fn, map(char2num, s))

用lambda函数进一步简化成:

1
2
3
4
5
6
7
from functools import reduce

def char2num(s):
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]

def str2int(s):
return reduce(lambda x, y: x * 10 + y, map(char2num, s))

filter

filter()函数用于过滤序列
接受一个函数和一个序列;

filter()把传入的函数一次作用于每个元素,然后根据返回值是True还是False决定保留还是求其该元素;

1
2
3
4
5
def is_odd(n):
return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]

删掉偶数

1
2
3
4
5
def not_empty(s):
return s and s.strip()

list(filter(not_empty, ['A', '', 'B', None, 'C', ' ']))
# 结果: ['A', 'B', 'C']

把一个序列中的空字符串删掉

sorted

排序算法

1
2
sorted([36, 5, -12, 9, -21])
# [-21, -12, 5, 9, 36]

sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:

1
2
sorted([36, 5, -12, 9, -21], key=abs)
# [5, 9, -12, -21, 36]
1
2
3
4
5
6
7
sorted(['bob', 'about', 'Zoo', 'Credit'])
# ['Credit', 'Zoo', 'about', 'bob']

# ------------------------------------

sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
# ['about', 'bob', 'Credit', 'Zoo']

要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True:

1
2
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
# ['Zoo', 'Credit', 'bob', 'about']

返回函数

  • 函数作为返回值
    高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回
1
2
3
4
5
6
7
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum

当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数

1
2
3
f = lazy_sum(1, 3, 5, 7, 9)
f
# <function lazy_sum.<locals>.sum at 0x101c6ed90>

正确姿势:

1
2
f()
# 25
  • 闭包
    返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs

f1, f2, f3 = count()

>>> f1()
9
>>> f2()
9
>>> f3()
9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs

>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9

参考资料:http://www.liaoxuefeng.com/