0%

Python - Theme 4 Functional Programming

函数式编程的一个特点是,允许把函数作为参数传入另一个函数,还允许返回一个函数。

1. 高阶函数

1.1. 变量可以指向函数

1
2
3
4
5
6
7
8
9
10
11
# 变量可以指向函数
>>> abs
<built-in function abs>
>>> abs(-6)
6

>>> f = abs
>>> f
<built-in function abs>
>>> f(-6)
6

1.2. 函数名也是变量

1
2
3
4
5
6
7
8
9
10
# 如果改变原指向函数的变量 abs 指向其他对象
>>> abs
<built-in function abs>
>>> abs = 6
>>> abs
6
>>> abs(-6)
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: 'int' object is not callable

1.3. 给函数传入函数

既然变量可以指向函数,函数的参数可以接收变量,因此一个函数就可以接收另一个函数作为参数,即–高阶函数。

1
2
3
4
>>> def addabsolute(x, y, f):
... return f(x) + f(y)
... print(addabsolute(-2, 2, abs))
4

1.4. map/reduce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# map(func, Iterable) -> Iterator. map将函数func依次作用在每一个Iterable元素上, 返回一个Iterator.
>>> def f(x):
... return x * x * x
...
>>> map(f,[1,2,3,4])
<map object at 0x11068c590>
>>> list(map(f,[1,2,3,4]))
[1, 8, 27, 64]
>>> for i in map(f,[1,2,3,4]):
... print(i)
...
1
8
27
64

# reduce(func, Iterable\Iterator)
# 连加
>>> from functools import reduce
>>> def add(x, y):
... return x + y
...
>>> reduce(add, [1,2,3,4,5])
15
# 连乘
>>> def prod(x, y):
... return x * y
...
>>> reduce(prod, [1,2,3,4,5])
120

1.5. fliter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# fliter(func, Iterable) -> Iterator. fliter 将函数作用于序列中的每一个元素,根据返回值是T/F来决定保留或去除该元素.

# 去除偶数
>>> def is_odd(n):
... return n % 2 == 1
...
>>> list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10]))
[1, 5, 9]

# 回文数 --<绝妙代码>
>>> def is_palindrome(n):
... return str(n) == str(n)[::-1]
...
>>> output = filter(is_palindrome, range(1, 1000))

1.6. sorted

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 高阶排序算法 - sorted()排序其实是实现了一个映射函数

# 1.对数字进行排序
>>> sorted([13,-2,9,35,-22,0,-0.5])
[-22, -2, -0.5, 0, 9, 13, 35]

# 2.接收key函数实现自定义排序
>>> sorted([13,-2,9,35,-22,0,-0.5], key=abs)
[0, -0.5, -2, 9, 13, -22, 35]

# 3.高阶用法
# 由于大写字母的ASCII比小写字母小
>>> sorted(['Bob', 'Aaron', 'Tom', 'Zoom', 'alen'])
['Aaron', 'Bob', 'Tom', 'Zoom', 'alen']
# 统一大小写进行比较
>>> sorted(['Bob', 'Aaron', 'Tom', 'Zoom', 'alen'], key=str.lower)
['Aaron', 'alen', 'Bob', 'Tom', 'Zoom']
# 排序翻转
>>> sorted(['Bob', 'Aaron', 'Tom', 'Zoom', 'alen'], key=str.lower, reverse=True)
['Zoom', 'Tom', 'Bob', 'alen', 'Aaron']

2. 返回函数

2.1. 高阶函数可将函数作为结果返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 累加函数
>>> def get_sum(*args):
... def cal_sum():
... s = 0
... for i in args:
... s = s + i
... return s
...
... return cal_sum
...
>>> f = get_sum(1,2,3,4,5)
>>> f #返回的是求和函数而并非求和结果
<function get_sum.<locals>.cal_sum at 0x1099d8dd0>
>>> f() # 调用函数,返回结果
15

# 在函数 get_sum 中定义和函数 cal_sum , 内部函数cal_sum可以引用外部函数get_sum的参数和局部变量;
# 当get_sum返回cal_sum时候,相应的参数和变量都保存在返回函数中,这种程序结构称为闭包: Closure;

2.2. 闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# 闭包结构中,返回的函数并没有立即执行, 调用f()才执行
>>> def squ():
... l = []
... for i in range(4):
... def f():
... return i*i
... l.append(f)
... return l
...

# 最终调用f()时候, i 已经为3了
>>> f1, f2, f3, f4 = squ()
>>> f1()
9
>>> f2()
9
>>> f3()
9
>>> f4()
9

# 闭包结构中, 返回函数不要引用任何循环变量, 或后续会发生改变的量
# 解决问题的方法是: 再创建一个函数, 计算出变量的值
>>> def squ():
... def f(j):
... def g():
... return j*j
... return g
...
... l = []
... for i in range(4):
... l.append(f(i))
... return l
...
>>> f1, f2, f3, f4 = squ()
>>> f1()
0
>>> f2()
1

# 闭包内函数修改外函数的局部变量 - nonlocal
#!/usr/bin/env python
# -*- coding : utf-8 -*-

x = 20
y = -20
z = 10


def outer():
x = 5
y = -5

def inner():
global x
x += 5
nonlocal y
y -= 5
z = 0
print("inner x:", x)
print("inner y:", y)
print("inner z:", z)
return inner


f = outer()
print("1."+"="*10)
f()
print("2."+"="*10)
f()
print("3."+"="*10)
f()

#输出
1.==========
inner x: 25
inner y: -10
inner z: 0
2.==========
inner x: 30
inner y: -15
inner z: 0
3.==========
inner x: 35
inner y: -20
inner z: 0

3. 匿名函数 - lambda

匿名函数无需显示的定义, 冒号前面的表示函数参数, 返回值即表达式的结果.

1
2
3
4
5
6
7
8
9
10
11
>>> list(filter(lambda x: x % 2 ==1, range(1, 10)))
[1, 3, 5, 7, 9]

# 匿名函数作为返回值
>>> def perimeter(x, y):
... return lambda : 2 * (x+y)
...
>>> perimeter(1,2)
<function perimeter.<locals>.<lambda> at 0x108c57ef0>
>>> perimeter(1,2)()
6

4. 装饰器 - decorator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# 装饰器其实就是一个返回函数的高阶函数
# 1.不含参数
#!/usr/bin/env python
# -*- coding : utf-8 -*-
import time
import functools


def process_time(func):
@functools.wraps(func) #相当于: wrapper.__name__ = func.__name__
def wrapper(*args, **kw):
start = time.time()
results = func(*args)
end = time.time()
costtime = end - start
print(func.__name__, "cost:", costtime)
return results
return wrapper


@process_time #相当于: squ = process_time(squ), 更换了原指向函数的变量squ的指向.
def squ(x):
return x*x


square = squ(5)
print('square =',square)
print('func name =',squ.__name__)

#输出
squ cost: 9.5367431640625e-07
square = 25
func name = squ

# 2.含参数
#!/usr/bin/env python
# -*- coding : utf-8 -*-
import time
import functools
import datetime


def process_time(date):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
start = time.time()
results = func(*args)
end = time.time()
costtime = end - start
print(date,func.__name__, "cost:", costtime)
return results
return wrapper
return decorator


@process_time(datetime.datetime.now()) #相当于: squ = process_time(datetime.datetime.now())(squ)
def squ(x):
return x*x


square = squ(5)
print('square =',square)
print('func name =',squ.__name__)

# 输出
2020-05-08 22:58:39.325757 squ cost: 1.1920928955078125e-06
square = 25
func name = squ

5. 偏函数 - Partial function

Python的 functools 模块提供了很多功能, 偏函数就是其一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 偏函数的主要作用即将含参的函数设置默认参数后生成新函数, 简化调用.
# int(value, base)
>>> int('10', base = 2)
2
>>> int('10', base = 16)
16

# 通过定义函数简化
>>> def int2(x, base=2):
... return int(x, base)
...
>>> int2('10')
2

# 偏函数
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('10')
2

# 偏函数可接收函数对象、*args和**kw.
1. **kw: base = 2 相当于 {'base': 2}
2. *args: '10' 相当于传给了 int(value, base) 函数的value.
>>> int2 = functools.partial(int, '10')
>>> int2()
10