Python 集合的遍历,推导及 filter/map/reduce 操作
借鉴于其他多数语言中集合的 map/reduce 操作,也想总结一下在 Python 中如何对集合进行 map/reduce。而不是对于 Python 集合只会用简单的 for ... in 遍历,处于之间的是 Python 的 Comprehension 操作,更倾向于译作推导; 在 Scala 中也有类似的 for-comprehension 语法。
因此本文将涉及到三个方面的知识,基本的集合遍历操作,集合的推导,与 filter/map/reduce 操作。我无法写出诸如 掌握 Python 集体看这一篇就够了 的文章,但基本由本篇出发能了解到 Python 集合的基本遍历,转换操作。其余如切片,和更多能作用于 Python 集合的函数未有提及, 请查阅相关文档。
集合的基本遍历操作
这一块主要是复习功课, 希望由此熟练掌握常用的集合遍历操作方式
基本遍历
1for a in [1, 2, 3]:<br/>
2 print(a)这个 for ... in 语法可应用于 list, tuple, 和 set, 还有 range, map 等。
不过上面的语法应用于 dict 只是对 key 进行遍历
1x = {"a": 1, "b": 2}
2for key in x: # 这里相当于是 for key in dict.keys():
3 print(key, "=>", x['key'])对于 dict 既然可以对 keys() 进行遍历,也就可以对值 values() 进行遍历
1x = {"a": 1, "b": 2}
2for value in x.values():
3 print(value) # 依次输出 1, 2x.keys() 的类型是 dict_keys,list(x.keys() 可以得到所有 key 组成的 list。set(x.keys() 得到相应的 set
x.values() 的类型是 dict_values。也可以用 list(x.values() 和 set(x.values() 转换为相应的 list 和 set
对 dict 同时遍历 key, value
1map = {"a": 1, "b": 2}
2for key, value in map.items():
3 print(key, "=>", value)map.items() 类型是 dict_items, 看下方
1>>> map.items()
2dict_items([('a', 1), ('b', 2)])
3>>> list(map.items())
4[('a', 1), ('b', 2)]
5>>> set(map.items())
6{('b', 2), ('a', 1)}因为每个元素是由 (key, value) 组成的 tuple, 所以能用 for key, value 进行拆解(unpack)。
遍历时得到索引
这时候要用到 enumerate 函数
1x = [1, 5, 3]
2for index, value in enumerate(x):
3 print(index, "=>", value)可以想像它实质遍历的是 list(enumerate(x)) 列表
1>>> list(enumerate(x))
2[(0, 1), (1, 5), (2, 3)]它的每一个元素是由索引与值组成的 tuple, 所以用 for index, value 来拆解。
集合的推导(comprehension)
Python 也无法在 for ... in 语句中应用 if..else 条件进行元素过滤,这就要用到推导了。Comprehension 可以同时完成集合的 filter 和 map 操作,如下
1x = [1, 5, 3]
2y = [val * 2 for val in x] # x 中每个元素乘以 2 得到新的列表 [2, 10, 6]
3z = [val * 2 for val in x if val > 2] # x 中大于 2 的元素诚以 2 得到新的列表 [10, 6]它的基本语法是
new_list = [expression for variable in old_list if expression]
new_dict = {key_expression: value_expression for variable in list if expression}
new_tuple = (expression for variable in old_tuple if expression) # 其中 old_tuple 也可以是 list
在 expression 部分加入条件
1x = [1, 2, 3, 4]
2>>> x = [1, 2, 3, 4]
3>>> y = [True if val % 2 == 0 else False for val in x]
4>>> y
5[False, True, False, True]Python 中的 value1 if condition else value2 语法我比较喜欢。
Comprehension 语法同样适用于 tuple, 例如下面的代码
1>>> a = (1, 3, 5)
2>>> b = (val + 2 for val in a)
3>>> list(b)
4[3, 5, 7]对字典的推导
基于前面的推导语法,我们可以应用到 dict 上
1>>> x = {"a": 1, "b": 5, "c": 3}
2>>> y = {key: value + 2 for key, value in x.items()}
3>>> y
4{'a': 3, 'b': 7, 'c': 5}
5>>> z = {key + "0": value + 2 for key, value in x.items() if value > 2}
6>>> z
7{'b0': 7, 'c0': 5}推导中可以有多个 for
1>>> a = [1, 3, 5]
2>>> b = [2, 4, 6]
3>>> x = [v1 * v2 for v1 in a for v2 in b]
4>>> x
5[2, 4, 6, 6, 12, 18, 10, 20, 30]上面得到一个 a 与 b 的笛卡尔乘积
Filter/Map/Reduce 操作
现在来到本文立意的初衷,就是为了了解 Python 的 filter/map/reduce 操作。其中 filter 和 map 是可直接使用的两个函数,reduce 是来自于 functools 模块,需用 from functools import reduce 引入。看到 functools 模块名,它里边还有不少好料。
Filter
对集合的过滤接受一个返回布尔值的 Predicate 函数,或者用内联 lambda 表达式的方式
1>>> x = [1, 3, 2, 5]
2>>> def greater_then_2(val):
3 return val > 2
4
5>>> y = filter(greater_then_2, x)
6>>> y
7<filter object at 0x1071f8048>
8>>> list(y)
9[3, 5]注意 filter 之后不是直接返回的 list, 而是一个 filter 类型,要用 list(y) 转换回 list 类型。
或者用内联 lambda 的方式
1>>> x = [1, 3, 2, 5]
2>>> y = filter(lambda a: a > 2, x)
3>>> list(y)
4[3, 5]
5>>> z = filter(lambda a: a > 2, filter(lambda a: a > 2, x))
6>>> list(z)
7[3, 5]filter 之后返回的虽然不是一个 list, 但是它能被用于下一轮的 filter. 可是又不能直接 for .. in 遍历 filter 类型。
1for val in y:
2 print(y)输出是
<filter object at 0x100e11160>
<filter object at 0x100e11160>
Map
有了 Filter 作铺垫之后,Map 就好理解了,只需把返回 True 或 False 的过滤函数改成转换函数。
1>>> x = [1, 3, 2, 5]
2>>> def plus_1(val):
3 return val + 1
4
5>>> y = map(plus_1, x)
6>>> y
7<map object at 0x1074b9160>
8>>> list(y)
9[2, 4, 3, 6]也是需用 list(y) 再转换回 list。也可以在 map 中用 lamba 表达式
1y = map(lambda a: a + 1, x)Reduce
reduce 通常才是真正执行计算的过程,前面 filter 和 map 多是准备, 整理输入数据的。由集合收缩为一个值就是 reduce 操作,当然也可以收缩为一个集合类型值。
比如说 1 加到 100 的操作就可以采用 reduce 操作,reduce 时可以从一个自定义的初始值开始,也可从集合中的第一个元素开始。
1>>> from functools import reduce
2>>> numbers = range(1, 101) # 下面直接对 range 进行 reduce 操作
3>>> def add(m, x): # m 代表每次累加后的结果,x 是当前遍历到的元素
4 return m + x
5
6>>>
7>>> y = reduce(add, numbers)
8>>> y
95050Python 的 reduce 操作默认从集合的第一个元素开始,因此对空列表是不能 reduce 的,取不到第一个元素
1>>> y = reduce(add, [])
2Traceback (most recent call last):
3 File "<pyshell#48>", line 1, in <module>
4 y = reduce(add, [])
5TypeError: reduce() of empty sequence with no initial valuereduce 也可以由第三个参数来提供初始值,如
1>>> y = reduce(add, [], 100)
2>>> y
3100reduce 方法的原型是
reduce(...)
reduce(function, sequence[, initial]) -> value
下面两个 reduce 用法是等效的
1reduce(function, sequence)
2reduce(function, sequence[1:], sequence[0]简单的 reduce 函数写成 lambda 表达式就行了。
链接:
永久链接 https://yanbin.blog/python-collection-map-reduce-operations/, 来自 隔叶黄莺 Yanbin's Blog[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。