Table of Contents
引子:写法对比
1"""
2以下代码的功能是获取给定的数组中所有的偶数
3"""
4all_numbers = [1, 2, 3, 4, 5, 6, 7, 8]
5# for循环写法
6even_numbers = []
7for number in all_numbers:
8 if number % 2 == 0:
9 even_numbers.append(number)
10
11# 列表推导式写法
12even_numbers = [number for number in all_numbers if number % 2 == 0]
上面两种的写法实现的功能是一致的。这意味着我们使用推导式的写法并不是出于功能需求,而是出于非功能需求,比如:代码更加易读、代码更加整洁等等。
为什么使用推导式
- 由于for循环的意思是
重复执行某些操作
,代表的是"How",如果我们使用的是for循环的写法来获取所有的偶数,那么对于阅读代码的人来说,需要查看for循环中到底进行了哪些操作,才能够得出结论:这段代码取了给定的所有整数,并将偶数放到了新列表中
。 - 而推导式的写法因为可以抽象为
变量名 = 推导式
,而赋值语句的意思是某个变量的值等于某个表达式/某个内容
,代表的是"What",所以如果我们使用的是推导式的写法,则对于阅读代码的人来说,这段代码的意思就是生成了某个内容,这个内容是给定所有整数中的所有偶数
。 - 也就是说,推导式的使用有助于代码阅读者更快更准确地知道代码片段的意图。
什么时候使用推导式
像上述引子中的情况一样,如果我们想要从当前的数据中重新生成满足某些条件的数据时,我们可以选择使用推导式。常见的比如:获取所有的奇数/偶数,生成所有数值的平方/某个计算结果,数据类型转换(列表转换成字典、字符串反序列化成python对象)等等
可以总结为两个要素:
- 我们代码段的意图是得到某个数据(列表/元组/集合/字典)
- 过程中如果涉及的条件判断或者计算或者函数调用需要简单
如果涉及的处理逻辑比较复杂,需要将处理逻辑独立成一个函数,然后在推导式中调用。比如调用标准库json.loads来反序列化数据:
1records: List[Dict] = [json.loads(json_content) for json_content in input_data]
明白了使用推导式的好处以及使用场景后,我们来介绍各个python原生容器类型的推导式如何写。 而介绍之后我们可以意识到各个容器类型的推导式语法基本一致,从中也可以看到python语法的简洁性和一致性。
列表推导式
列表的语法为: [process_logic(element) for element in container if conditon_is_met(element)]
列表推导式就是将for循环的循环体用中括号包裹起来,然后将for条件放在列表末尾,如下:
1"""生成元素相同的新列表"""
2>>> [n for n in [1, 2, 3]]
3[1, 2, 3]
4
5"""生成所有值是原值两倍的列表"""
6>>> [n * 2 for n in [1, 2, 3]]
7[2, 4, 6]
8
9"""生成所有值是原值平方的列表"""
10>>> [n**2 for n in range(10)]
11[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
12
13"""获取原列表中所有的偶数"""
14>>> [n for n in [1, 2, 3] if n % 2]
15[1, 3]
元组推导式
将列表推导式的中括号改为小括号即可变成元组推导式。
但是,(expression code)
这个语法糖已经先被迭代器占用了,所以生成的其实是元组推导式对应的迭代器而不是元组。
如果要获得元组,需要用tuple()
方法进行转换:
1"""查看小括号语法糖生成的对象类型"""
2>>> print(type((number for number in range(10))))
3<class 'generator'>
4>>> print((number for number in range(10)))
5<generator object <genexpr> at 0x00857F08>
6
7"""转换成元组"""
8>>> print(tuple((number for number in range(10))))
9(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
10
11"""tuple()包括后, 内层的括号可以省略"""
12>>> print(tuple(number for number in range(10)))
13(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
14
15"""获取所有的偶数"""
16>>> print(tuple(number for number in range(10) if number % 2 == 0))
17(0, 2, 4, 6, 8)
集合推导式
集合推导式的写法,就是将列表推导式的中括号改为花括号即可,语法如下:
{process_logic(element) for element in container if conditon_is_met(element)}
例子如下:
1"""生成所有元素的集合"""
2>>> {s for s in [1, 2, 1, 0]}
3set([0, 1, 2])
4
5"""生成所有元素平方的集合"""
6>>> {s**2 for s in [1, 2, 1, 0]}
7set([0, 1, 4])
8>>> {s**2 for s in range(10)}
9set([0, 1, 4, 9, 16, 25, 36, 49, 64, 81])
10
11"""生成所有奇数元素的集合"""
12>>> {s for s in [1, 2, 3] if s % 2}
13set([1, 3])
字典推导式
字典推导式和集合推导式相似,只是元素需要写成键值对(键: 值
)的形式,如下:
{key_logic(element): value_logic for element in container if conditon_is_met(element)}
例子如下:
1"""从元素为二元组的列表生成字典"""
2>>> {k: v for k, v in [(1, 2), (3, 4)]}
3{1: 2, 3: 4}
4
5"""从元素为二元组的元组生成字典"""
6>>> {k: v for k, v in (('I', 1), ('II', 2))}
7{'I': 1, 'II': 2}
8
9"""从数值迭代器生成键等于值的字典"""
10>>> {n: n for n in range(2)}
11{0: 0, 1: 1}
12
13"""生成键为ascii码字符, 值为对应ascii码值的字典"""
14>>> {chr(n): n for n in (65, 66, 66)}
15{'A': 65, 'B': 66}
16
17
18"""将元素为二元组的元组中满足条件的所有元素转换成字典"""
19>>> {k: v for k, v in (('a', 0), ('b', 1)) if v == 1}
20{'b': 1}
多重推导式/嵌套推导式
其实就是多重循环如何改写成推导式的问题。写法也很简单,就是将多重循环按照外层到内层的顺序拼成一行,反正推导式内,如下:
[process(element_1, element_2) for element_1 in items_1 for element_2 in items_2]
等于以下多重循环的写法:
1results = [] 2for element_1 in items_1: 3 for element_2 in items_2: 4 results.append(process(element_1, element_2))
例子如下:
1"""
2生成字符在前, 数值在后的组合
3由于对字符的遍历在前, 所有字符相同的元素会聚在一起
4"""
5
6>>> [f"{char}{number}" for char in "ABCD" for number in range(4)]
7['A0', 'A1', 'A2', 'A3', 'B0', 'B1', 'B2', 'B3', 'C0', 'C1', 'C2', 'C3', 'D0', 'D1', 'D2', 'D3']
8
9"""
10生成字符在前, 数值在后的组合
11由于对数值的遍历在前, 所有数值相同的元素会聚在一起
12"""
13>>> [f"{char}{number}" for number in range(4) for char in "ABCD"]
14['A0', 'B0', 'C0', 'D0', 'A1', 'B1', 'C1', 'D1', 'A2', 'B2', 'C2', 'D2', 'A3', 'B3', 'C3', 'D3']
15
16
17"""生成二元组集合"""
18>>> {(m, n) for n in range(2) for m in range(3, 5)}
19set([(3, 0), (3, 1), (4, 0), (4, 1)])
三重循环的推导式会写了吗?
1>>> [f"{first}{second}{third}" for first in "AB" for second in range(2) for third in "XY"]
2['A0X', 'A0Y', 'A1X', 'A1Y', 'B0X', 'B0Y', 'B1X', 'B1Y']
学会了吗?(●ˇ∀ˇ●)
好书推荐:
好课推荐:
写文不易,如果对你有帮助的话,来一波点赞、收藏、关注吧~👇