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