近期公司有项目上使用的代码涉及调用外部API接口进行多页请求,并需要把对应的数据返回后进行处理。开发人员使用了多层for嵌套循环处理,从逻辑上看,确实for循环比较简单也比较容易理解,不过性能上会略差一些。在进行响应时延过高的问题分析时,通过换用map函数代替for可以进行效率的提升。本篇就总结下python下的map、reduce方法。
一、map方法
map()方法会将 一个函数 映射到序列的每一个元素上,生成新序列,包含所有函数返回值。他传入的参数是一个函数和一个list序列。在python2.X中 map函数返回的是list列表,在python3新版本里返回的是迭代器,需要通过list(map(fun1,list1))才可以返回结果。
map(function_to_apply, list_of_inputs)
function_to_apply代表函数、list_of_inputs代表输入序列。比如,实现一个求平方的操作:
items = [1, 2, 3, 4, 5] squared = list(map(lambda x: x**2, items)) # 等价于下面的写法 items = [1, 2, 3, 4, 5] def f(x): return x**2 squared = list(map(f, items)) # 等价于以下for循环的写法 items = [1, 2, 3, 4, 5] # 列表 squared = [] for i in items: squared.append(i**2)
上面的函数,使用map内部实现的原理如下:
map()方法会将 一个函数 映射到序列的每一个元素上,生成新序列,包含所有函数返回值。也就是说序列里每一个元素都被当做x变量,放到一个函数f(x)里,其结果是f(x1)、f(x2)、f(x3)……组成的新序列。
二、reduce函数
相比于map,reduce的操作稍稍难理解一点点。它也是规定一个映射,不过不是将一个元素映射成一个结果。而是将两个元素归并成一个结果。并且它并不是调用一次,而是依次调用,直到最后只剩下一个结果为止。其语法如下:
reduce(function, iterable[, initializer]) function:代表函数 iterable:序列 initializer:初始值(可选)
以下列出个实现阶乘的示例如下:
# 导入reduce from functools import reduce # 定义函数 def f(x,y): return x*y # 定义序列,含1~10的元素 items = range(1,11) # 使用reduce方法 result = reduce(f,items) print(result)
依次求和的示例如下:
from functools import reduce def f(a, b): return a + b print(reduce(f, [1, 2, 3, 4])) # 其等价于以下实现 print(reduce(lambda x, y: x + y, [1, 2, 3, 4]))
其内部执行原理如下:
map和reduce相同的是接受传入的参数都是函数和list参数,不过reduce可以接受第三个参。
三、map与reduce结合
在大数据里会有一个mapreduce概念,其实在python里两个函数结合起来,确实可以达到mapreduce的效果,这里给出一个代词数量统计的示例,代码如下:
from collections import Counter texts = ['apple bear peach grape', 'grape orange pear'] def mp(text): words = text.split(' ') return Counter(words) print(reduce(lambda x, y: x + y, map(mp, texts)))
执行结果如下:
>>> print(reduce(lambda x, y: x + y, map(mp, texts))) Counter({'grape': 2, 'apple': 1, 'bear': 1, 'peach': 1, 'orange': 1, 'pear': 1})
四、filter函数与compress函数
filter的英文是过滤,所以它的使用就很明显了。它的用法和map有些类似,我们编写一个函数来判断元素是否合法。通过调用filter,会自动将这个函数应用到容器当中所有的元素上,最后只会保留运行结果是True的元素,而过滤掉那些是False的元素。例如,以下通过推导式进行奇数提取的,就可以通过filter函数实现提取:
arr = [1, 3, 2, 4, 5, 8] [i for i in arr if i % 2 > 0 ] #使用filter实现如下: list(filter(lambda x: x % 2 > 0, arr))
和filter函数类似的,还有一个compress函数,其是在itertools包里的一个函数。可以通过传给的布尔值列表,找到值为真的结果:
from itertools import compress student = ['xiaoming', 'xiaohong', 'xiaoli', 'emily'] scores = [60, 70, 80, 40] >>> pass = [i > 60 for i in scores] >>> print(pass) [False, True, True, False] >>> list(compress(student, pass)) ['xiaohong', 'xiaoli']
五、写在最后
python已经帮我们造好了很多比较容易实现的方法,可以通过引用这些方法,快速的达到我们想法实现的目标,在实际操作过程中,要尽可能的避免使用大的for循环嵌套。最后再列一个示例,可以替代for循环的总结:
numbers = [1,2,3,4,5,6] odd_numbers = [] squared_odd_numbers = [] total = 0 # filter for odd numbers for number in numbers: if number % 2 == 1: odd_numbers.append(number) # square all odd numbers for number in odd_numbers: squared_odd_numbers.append(number * number) # calculate total for number in squared_odd_numbers: total += number # calculate average #上面的内容,可以通过如下函数简单实现: from functools import reduce numbers = [1,2,3,4,5,6] odd_numbers = filter(lambda n: n % 2 == 1, numbers) squared_odd_numbers = map(lambda n: n * n, odd_numbers) total = reduce(lambda acc, n: acc + n, squared_odd_numbers)
python中的map/reduce/filter函数,首发于运维之路。
转载请注明:我是IT » python中的map/reduce/filter函数