'python 2.7学习笔记(4)'

[TOC]

函数式编程

高阶函数

变量可以指向函数
1
2
3
f=abs
f(-10)
10
函数名也是变量
1
2
abs=10
abs(-10) #出错
传入函数

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

1
2
def add(a,b,f):
return f(a)+f(b)
1
add(2,-4,abs);  # 6
map/reduce
map

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

1
2
3
4
5
>>> def f(x):
... return x * x
...
>>> map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]

1
2
>>> map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])
['1', '2', '3', '4', '5', '6', '7', '8', '9']
reduce

reduce把一个函数作用在一个序列[x1, x2, x3…]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

1
2
3
4
5
>>> def add(x, y):
... return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
25

把str转换成int

1
2
3
4
5
6
7
8
>>> 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]
...
>>> reduce(fn, map(char2num, '13579'))
13579

整理成一个str2int的函数就是:

1
2
3
4
5
6
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
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

用于过滤序列,与Map类似,也接受一个函数和一个list,只不过filter把传入的函数依次作用于每个元素,返回的是true或者false,决定保留或者丢弃该元素。

只保留一个序列中的偶数

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

如下,把一个序列中的空字符串去掉

1
2
>>> filter(lambda s:s and s.strip(),['A','B','',None,'C',''])
['A', 'B', 'C']

删除1~100的素数

1
2
3
4
5
6
7
8
9
10
11
def isSushu(x):
i=1
while i<x-1:
i=i+1
if x%i==0:
return True
return False

print filter(isSushu,range(1,101))

[4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 22, 24, 25, 26, 27, 28, 30, 32, 33, 34, 35, 36, 38, 39, 40, 42, 44, 45, 46, 48, 49, 50, 51, 52, 54, 55, 56, 57, 58, 60, 62, 63, 64, 65, 66, 68, 69, 70, 72, 74, 75, 76, 77, 78, 80, 81, 82, 84, 85, 86, 87, 88, 90, 91, 92, 93, 94, 95, 96, 98, 99, 100]

sorted

排序

1
2
>>> sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]
1
2
3
4
5
6
7
8
9
def reversed_cmp(x, y):
if x > y:
return -1
if x < y:
return 1
return 0

>>> sorted([36, 5, 12, 9, 21], reversed_cmp)
[36, 21, 12, 9, 5]

返回函数

函数作为返回值

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。

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
4
5
>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function sum at 0x10452f668>
>>> f()
25

在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。

返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变。

匿名函数

1
2
>>> map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]

关键字lambda表示匿名函数,冒号前面的x表示函数参数。
匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。

装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# -*- coding: utf-8 -*-
import functools

def log(func):
@functools.wraps(func)
def wrapper(*args,**kw):
print 'begin call'
def end(func):
func(*args,**kw)
print 'end call'
return
return end(func)
return wrapper

@log
def now():
print 'now is \'2017-01-19\''
now()
##########
G:\python文件>python second.py
begin call
now is '2017-01-19'
end call

把@log放到now()函数的定义处,相当于执行了语句:
now=log(now)
把原始函数的name等属性复制到wrapper()函数中,Python内置的functools.wraps就是干这个事的

偏函数

functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。

转换为二进制

1
2
3
4
5
6
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85

创建偏函数时,实际上可以接收函数对象、*args和**kw这3个参数

int2 = functools.partial(int, base=2)
int2('10010')
相当于:

1
2
kw = { base: 2 }
int('10010', **kw)

1
2
3
4
5
6
max2 = functools.partial(max, 10)   #实际上会把10作为*args的一部分自动加到左边
max2(5, 6, 7)
#相当于
args = (10, 5, 6, 7)
max(*args)
#结果为10