map()、reduce()、filter()是Python中很常用的几个函数,也是Python支持函数式编程的重要体现。不过,在Python 3.x中,reduce()不是内置函数,而是放到了标准库functools中,需要先导入再使用。
(1)map()。内置函数map()可以将一个函数依次映射到序列或迭代器对象的每个元素上,并返回一个可迭代的map对象作为结果,map对象中每个元素是原序列中元素经过该函数处理后的结果,该函数不对原序列或迭代器对象做任何修改。
>>> list(map(str, range(5))) #把列表中元素转换为字符串
['0', '1', '2', '3', '4']
>>> def add5(v): #单参数函数
return v+5
>>> list(map(add5, range(10))) #把单参数函数映射到一个序列的所有元素
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
>>> def add(x, y): #可以接收2个参数的函数
return x+y
>>> list(map(add, range(5), range(5,10))) #把双参数函数映射到两个序列上
[5, 7, 9, 11, 13]
>>> list(map(lambda x, y: x+y, range(5), range(5,10)))
[5, 7, 9, 11, 13]
>>> def myMap(iterable, op, value): #自定义函数
if op not in '+-*/': #实现序列与数字的四则运算
return 'Error operator'
func = lambda i:eval(repr(i)+op+repr(value))
return map(func, iterable)
>>> list(myMap(range(5), '+', 5))
[5, 6, 7, 8, 9]
>>> list(myMap(range(5), '-', 5))
[-5, -4, -3, -2, -1]
>>> list(myMap(range(5), '*', 5))
[0, 5, 10, 15, 20]
>>> import random
>>> x = random.randint(1, 1e30) #生成指定范围内的随机整数
>>> x
839746558215897242220046223150
>>> list(map(int, str(x))) #提取大整数每位上的数字
[8, 3, 9, 7, 4, 6, 5, 5, 8, 2, 1, 5, 8, 9, 7, 2, 4, 2, 2, 2, 0, 0, 4, 6, 2, 2, 3, 1, 5, 0]
(2)reduce()。标准库functools中的函数reduce()可以将一个接收2个参数的函数以迭代累积的方式从左到右依次作用到一个序列或迭代器对象的所有元素上,并且允许指定一个初始值。例如,reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])计算过程为((((1+2)+3)+4)+5),第一次计算时x为1而y为2,再次计算时x的值为(1+2)而y的值为3,再次计算时x的值为((1+2)+3)而y的值为4,以此类推,最终完成计算并返回((((1+2)+3)+4)+5)的值。
>>> from functools import reduce
>>> seq = list(range(1, 10))
>>> reduce(add, seq) #add是上一段代码中定义的函数
45
>>> reduce(lambda x, y: x+y, seq) #使用lambda表达式实现相同功能
45
上面这两段代码的功能是一样的,执行过程如下图:
>>> import operator #标准库operator提供了大量运算
>>> operator.add(3,5) #可以像普通函数一样直接调用
8
>>> reduce(operator.add, seq) #使用add运算
45
>>> reduce(operator.add, seq, 5) #指定累加的初始值为5
50
>>> reduce(operator.mul, seq) #乘法运算
362880
>>> reduce(operator.mul, range(1, 6)) #5的阶乘
120
>>> reduce(operator.add, map(str, seq)) #转换成字符串再累加
'123456789'
>>> ''.join(map(str, seq)) #使用join()方法实现字符串连接
'123456789'
>>> reduce(operator.add, [[1, 2], [3], [4]], []) #这个操作占用空间较大,慎用
[1, 2, 3, 4]
与上面代码中最后演示的用法类似,作为一种技巧,reduce()函数还支持下面的用法(感谢浙江省浦江中学方春林老师提供本例用法):
>>> from random import randint
>>> lst = [randint(1, 10) for i in range(50)] #随机数列表
>>> def tjNum(dic, k): #统计元素出现次数
if k in dic:
dic[k] += 1
else:
dic[k] = 1
return dic
>>> from functools import reduce
>>> reduce(tjNum, lst, {})
{1: 6, 2: 3, 3: 6, 4: 3, 5: 4, 6: 7, 7: 5, 8: 5, 9: 6, 10: 5}
(3)filter()。内置函数filter()将一个单参数函数作用到一个序列上,返回该序列中使得该函数返回值为True的那些元素组成的filter对象,如果指定函数为None,则返回序列中等价于True的元素。
>>> seq = ['foo', 'x41', '?!', '***']
>>> def func(x):
return x.isalnum() #测试是否为字母或数字
>>> filter(func, seq) #返回filter对象
<filter object at 0x000000000305D898>
>>> list(filter(func, seq)) #把filter对象转换为列表
['foo', 'x41']
>>> [x for x in seq if x.isalnum()] #使用列表推导式实现相同功能
['foo', 'x41']
>>> list(filter(lambda x: x.isalnum(), seq)) #使用lambda表达式实现相同功能
['foo', 'x41']
>>> list(filter(None, [1, 2, 3, 0, 0, 4, 0, 5])) #指定函数为None
[1, 2, 3, 4, 5]