「python|语言特性」为什么python的推导式语法一用就上瘾?

本文主要介绍python语言特性中推导式语法,包括什么时候使用推导式,为什么使用推导式,列表推导式、元组推导式、集合推导式、字典推导式、以及多重推导式的写法

文章目录

  • 引子:写法对比
  • 为什么使用推导式
  • 什么时候使用推导式
  • 列表推导式
  • 元组推导式
  • 集合推导式
  • 字典推导式
  • 多重推导式/嵌套推导式
  • 三重循环的推导式会写了吗?

引子:写法对比

"""
以下代码的功能是获取给定的数组中所有的偶数
"""
all_numbers = [1, 2, 3, 4, 5, 6, 7, 8]
# for循环写法
even_numbers = []
for number in all_numbers:
	if number % 2 == 0:
		even_numbers.append(number)

# 列表推导式写法
even_numbers = [number for number in all_numbers if number % 2 == 0]

上面两种的写法实现的功能是一致的。这意味着我们使用推导式的写法并不是出于功能需求,而是出于非功能需求,比如:代码更加易读、代码更加整洁等等

为什么使用推导式

  • 由于for循环的意思是重复执行某些操作,代表的是"How",如果我们使用的是for循环的写法来获取所有的偶数,那么对于阅读代码的人来说,需要查看for循环中到底进行了哪些操作,才能够得出结论:这段代码取了给定的所有整数,并将偶数放到了新列表中
  • 而推导式的写法因为可以抽象为变量名 = 推导式,而赋值语句的意思是某个变量的值等于某个表达式/某个内容,代表的是"What",所以如果我们使用的是推导式的写法,则对于阅读代码的人来说,这段代码的意思就是生成了某个内容,这个内容是给定所有整数中的所有偶数
  • 也就是说,推导式的使用有助于代码阅读者更快更准确地知道代码片段的意图。

什么时候使用推导式

像上述引子中的情况一样,如果我们想要从当前的数据中重新生成满足某些条件的数据时,我们可以选择使用推导式。常见的比如:获取所有的奇数/偶数,生成所有数值的平方/某个计算结果,数据类型转换(列表转换成字典、字符串反序列化成python对象)等等

可以总结为两个要素:

  • 我们代码段的意图是得到某个数据(列表/元组/集合/字典)
  • 过程中如果涉及的条件判断或者计算或者函数调用需要简单

如果涉及的处理逻辑比较复杂,需要将处理逻辑独立成一个函数,然后在推导式中调用。比如调用标准库json.loads来反序列化数据:

records: 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条件放在列表末尾,如下:

"""生成元素相同的新列表"""
>>> [n for n in [1, 2, 3]]
[1, 2, 3]

"""生成所有值是原值两倍的列表"""
>>> [n * 2 for n in [1, 2, 3]]
[2, 4, 6]

"""生成所有值是原值平方的列表"""
>>> [n**2 for n in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

"""获取原列表中所有的偶数"""
>>> [n for n in [1, 2, 3] if n % 2]
[1, 3]

元组推导式

将列表推导式的中括号改为小括号即可变成元组推导式。
但是,(expression code)这个语法糖已经先被迭代器占用了,所以生成的其实是元组推导式对应的迭代器而不是元组。
如果要获得元组,需要用tuple()方法进行转换:

"""查看小括号语法糖生成的对象类型"""
>>> print(type((number for number in range(10))))
<class 'generator'>
>>> print((number for number in range(10)))
<generator object <genexpr> at 0x00857F08>

"""转换成元组"""
>>> print(tuple((number for number in range(10))))
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

"""tuple()包括后, 内层的括号可以省略"""
>>> print(tuple(number for number in range(10)))
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

"""获取所有的偶数"""
>>> print(tuple(number for number in range(10) if number % 2 == 0))
(0, 2, 4, 6, 8)

集合推导式

集合推导式的写法,就是将列表推导式的中括号改为花括号即可,语法如下:
{process_logic(element) for element in container if conditon_is_met(element)}
例子如下:

"""生成所有元素的集合"""
>>> {s for s in [1, 2, 1, 0]}
set([0, 1, 2])

"""生成所有元素平方的集合"""
>>> {s**2 for s in [1, 2, 1, 0]}
set([0, 1, 4])
>>> {s**2 for s in range(10)}
set([0, 1, 4, 9, 16, 25, 36, 49, 64, 81])

"""生成所有奇数元素的集合"""
>>> {s for s in [1, 2, 3] if s % 2}
set([1, 3])

字典推导式

字典推导式和集合推导式相似,只是元素需要写成键值对(键: 值)的形式,如下:
{key_logic(element): value_logic for element in container if conditon_is_met(element)}
例子如下:

"""从元素为二元组的列表生成字典"""
>>> {k: v for k, v in [(1, 2), (3, 4)]}
{1: 2, 3: 4}

"""从元素为二元组的元组生成字典"""
>>> {k: v for k, v in (('I', 1), ('II', 2))}
{'I': 1, 'II': 2}

"""从数值迭代器生成键等于值的字典"""
>>> {n: n for n in range(2)}
{0: 0, 1: 1}

"""生成键为ascii码字符, 值为对应ascii码值的字典"""
>>> {chr(n): n for n in (65, 66, 66)}
{'A': 65, 'B': 66}


"""将元素为二元组的元组中满足条件的所有元素转换成字典"""
>>> {k: v for k, v in (('a', 0), ('b', 1)) if v == 1}
{'b': 1}

多重推导式/嵌套推导式

其实就是多重循环如何改写成推导式的问题。写法也很简单,就是将多重循环按照外层到内层的顺序拼成一行,反正推导式内,如下:
[process(element_1, element_2) for element_1 in items_1 for element_2 in items_2]
等于以下多重循环的写法:

results = []
for element_1 in items_1:
	for element_2 in items_2:
		results.append(process(element_1, element_2))

例子如下:

"""
生成字符在前, 数值在后的组合
由于对字符的遍历在前, 所有字符相同的元素会聚在一起
"""

>>> [f"{char}{number}" for char in "ABCD" for number in range(4)]
['A0', 'A1', 'A2', 'A3', 'B0', 'B1', 'B2', 'B3', 'C0', 'C1', 'C2', 'C3', 'D0', 'D1', 'D2', 'D3']

"""
生成字符在前, 数值在后的组合
由于对数值的遍历在前, 所有数值相同的元素会聚在一起
"""
>>> [f"{char}{number}" for number in range(4) for char in "ABCD"]
['A0', 'B0', 'C0', 'D0', 'A1', 'B1', 'C1', 'D1', 'A2', 'B2', 'C2', 'D2', 'A3', 'B3', 'C3', 'D3']


"""生成二元组集合"""
>>> {(m, n) for n in range(2) for m in range(3, 5)}
set([(3, 0), (3, 1), (4, 0), (4, 1)])

三重循环的推导式会写了吗?

>>> [f"{first}{second}{third}" for first in "AB" for second in range(2) for third in "XY"]
['A0X', 'A0Y', 'A1X', 'A1Y', 'B0X', 'B0Y', 'B1X', 'B1Y']

学会了吗?(●ˇ∀ˇ●)

好书推荐:

  • 流畅的python
  • Python编程 从入门到实践 第2版
  • Python数据结构与算法分析 第2版

好课推荐:

  • 零基础学python
  • python核心技术与实战
  • python自动化办公实战

写文不易,如果对你有帮助的话,来一波点赞、收藏、关注吧~

你可能感兴趣的:(给程序员看的python教程,python,python,开发语言)